ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-inet-connection.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-inet-connection.c - Functions for creating and destroying Internet
3    connections.
4
5    Copyright (C) 1999 Free Software Foundation
6
7    The Gnome Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11
12    The Gnome Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16
17    You should have received a copy of the GNU Library General Public
18    License along with the Gnome Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.
21
22    Author: Ettore Perazzoli <ettore@gnu.org> */
23
24 #include <config.h>
25 #include "gnome-vfs-inet-connection.h"
26 #include "gnome-vfs-private-utils.h"
27
28 #include <errno.h>
29 #include <glib/gmem.h>
30 #include <glib/gmessages.h>
31 #include <string.h>
32 /* Keep <sys/types.h> above the network includes for FreeBSD. */
33 #include <sys/types.h>
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <sys/socket.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39
40 extern int h_errno;
41
42 struct GnomeVFSInetConnection {
43 #ifdef ENABLE_IPV6
44         struct sockaddr_in6 addr6;
45 #endif
46         struct sockaddr_in addr;
47         guint sock;
48         guint socklen;
49 };
50
51 /**
52  * gnome_vfs_inet_connection_create:
53  * @connection_return: pointer to a GnomeVFSInetConnection, which will
54  * contain an allocated GnomeVFSInetConnection object on return.
55  * @host_name: string indicating the host to establish an internet connection with
56  * @host_port: the port number to connect to
57  * @cancellation: handle allowing cancellation of the operation
58  *
59  * Creates a connection at @handle_return to @host_name using
60  * port @port.
61  *
62  * Return value: GnomeVFSResult indicating the success of the operation
63  **/
64 GnomeVFSResult
65 gnome_vfs_inet_connection_create (GnomeVFSInetConnection **connection_return,
66                                   const gchar *host_name,
67                                   guint host_port,
68                                   GnomeVFSCancellation *cancellation)
69 {
70         GnomeVFSInetConnection *new;
71 #ifdef ENABLE_IPV6
72         struct addrinfo hints, *result, *res;
73         int ret;
74 #endif
75         struct hostent *host_info;
76         struct sockaddr_in addr;
77         gint sock;
78
79         g_return_val_if_fail (connection_return != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
80         g_return_val_if_fail (host_name != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
81         g_return_val_if_fail (host_port != 0, GNOME_VFS_ERROR_BAD_PARAMETERS);
82
83 #ifdef ENABLE_IPV6
84         if (_gnome_vfs_have_ipv6 ()) {
85                 result = NULL;
86                 ret = 0;
87                 sock = 0;
88
89                 memset (&hints, 0, sizeof (hints));
90                 hints.ai_socktype = SOCK_STREAM;
91
92                 if (getaddrinfo (host_name, NULL, &hints, &result) != 0) {
93                         return GNOME_VFS_ERROR_HOST_NOT_FOUND;
94                 }
95
96                 if (gnome_vfs_cancellation_check (cancellation)) {
97                         return GNOME_VFS_ERROR_CANCELLED;
98                 }
99
100                 /*For selecting a valid IP of the List*/
101                 for (res = result; res; res = res->ai_next) {
102
103                         if (res->ai_family != AF_INET && res->ai_family != AF_INET6) {
104                                 continue;
105                         }
106
107                         sock = socket (res->ai_family, SOCK_STREAM, 0);
108                         if (sock < 0) {
109                                 continue;
110                         }
111
112                         if (res->ai_family == AF_INET) {
113                                 ((struct sockaddr_in *)res->ai_addr)->sin_port = htons (host_port);
114                         }
115
116                         if (res->ai_family == AF_INET6) {
117                                 ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons (host_port);
118                         }
119
120                         ret = connect (sock, res->ai_addr, res->ai_addrlen);
121                         if (ret == 0) {
122                                 break;
123                         }
124
125                         close (sock);
126                 }
127
128                 if (!res) {
129                         freeaddrinfo (result);
130
131                         if (sock < 0 || ret < 0) {
132                                 /*Error in connection or socket creation.*/
133                                 return gnome_vfs_result_from_errno ();
134                         } else {
135                                 /*Error: No IPv4 or IPv6 address.*/
136                                 return GNOME_VFS_ERROR_HOST_NOT_FOUND;
137                         }
138                 }
139
140                 new = g_new (GnomeVFSInetConnection, 1);
141
142                 if (res->ai_family == AF_INET) { 
143                         memcpy (&new->addr, res->ai_addr, res->ai_addrlen);
144                 }
145
146                 if (res->ai_family == AF_INET6) {
147                         memcpy (&new->addr6, res->ai_addr, res->ai_addrlen);
148                 }
149
150                 new->socklen = res->ai_addrlen;
151                 new->sock = sock;
152                 freeaddrinfo (result);
153         }
154         else
155 #endif
156         {
157                 sock = socket (PF_INET, SOCK_STREAM, 0);
158                 if (sock < 0) {
159                         return gnome_vfs_result_from_errno ();
160                 }
161
162                 host_info = gethostbyname (host_name);
163                 if (gnome_vfs_cancellation_check (cancellation)) {
164                         return GNOME_VFS_ERROR_CANCELLED;
165                 }
166
167                 if (host_info == NULL) {
168                         return gnome_vfs_result_from_h_errno ();
169                 }
170
171                 addr.sin_family = host_info->h_addrtype;
172                 addr.sin_addr = * (struct in_addr *) host_info->h_addr;
173                 addr.sin_port = htons (host_port);
174
175                 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
176                         return gnome_vfs_result_from_errno ();
177                 }
178
179                 new = g_new (GnomeVFSInetConnection, 1);
180                 memcpy (&new->addr, &addr, sizeof (addr));
181                 new->socklen = sizeof(addr);
182                 new->sock = sock;
183
184         }
185         *connection_return = new;
186         return GNOME_VFS_OK;
187 }
188
189
190 /**
191  * gnome_vfs_inet_connection_destroy:
192  * @connection: connection to destroy
193  * @cancellation: handle for cancelling the operation
194  *
195  * Closes/Destroys @connection.
196  **/
197 void
198 gnome_vfs_inet_connection_destroy (GnomeVFSInetConnection *connection,
199                                    GnomeVFSCancellation   *cancellation)
200 {
201         g_return_if_fail (connection != NULL);
202
203         close (connection->sock);
204         g_free (connection);
205 }
206
207 /**
208  * gnome_vfs_inet_connection_close:
209  * @connection: connection to close
210  *
211  * Closes @connection, freeing all used resources.
212  **/
213 static void
214 gnome_vfs_inet_connection_close (GnomeVFSInetConnection *connection)
215 {
216         gnome_vfs_inet_connection_destroy (connection, NULL);
217 }
218
219 /**
220  * gnome_vfs_inet_connection_get_fd:
221  * @connection: connection to get the file descriptor from
222  *
223  * Retrieve the UNIX file descriptor corresponding to @connection.
224  *
225  * Return value: file descriptor
226  **/
227 gint 
228 gnome_vfs_inet_connection_get_fd (GnomeVFSInetConnection *connection)
229 {
230         g_return_val_if_fail (connection != NULL, -1);
231         return connection->sock;
232 }
233
234 /* SocketImpl for InetConnections */
235
236 /**
237  * gnome_vfs_inet_connection_read:
238  * @connection: connection to read data from
239  * @buffer: allocated buffer of at least @bytes bytes to be read into
240  * @bytes: number of bytes to read from @socket into @buffer
241  * @bytes_read: pointer to a GnomeVFSFileSize, will contain
242  * the number of bytes actually read from the socket on return.
243  *
244  * Read @bytes bytes of data from @connection into @buffer.
245  *
246  * Return value: GnomeVFSResult indicating the success of the operation
247  **/
248 static GnomeVFSResult 
249 gnome_vfs_inet_connection_read (GnomeVFSInetConnection *connection,
250                                 gpointer buffer,
251                                 GnomeVFSFileSize bytes,
252                                 GnomeVFSFileSize *bytes_read)
253 {
254         gint read_val;
255
256         do {
257                 read_val = read (connection->sock, buffer, bytes);
258         } while (read_val == -1 && errno == EINTR);
259
260         if (read_val == -1) {
261                 *bytes_read = 0;
262                 return gnome_vfs_result_from_errno ();
263         } else {
264                 *bytes_read = read_val;
265         }
266
267         return bytes_read == 0 ? GNOME_VFS_ERROR_EOF : GNOME_VFS_OK;
268 }
269
270 /**
271  * gnome_vfs_inet_connection_write:
272  * @connection: connection to write data to
273  * @buffer: data to write to the connection
274  * @bytes: number of bytes from @buffer to write to @socket
275  * @bytes_written: pointer to a GnomeVFSFileSize, will contain
276  * the number of bytes actually written to the connection on return.
277  *
278  * Write @bytes bytes of data from @buffer to @connection.
279  *
280  * Return value: GnomeVFSResult indicating the success of the operation
281  **/
282 static GnomeVFSResult 
283 gnome_vfs_inet_connection_write (GnomeVFSInetConnection *connection,
284                                  gconstpointer buffer,
285                                  GnomeVFSFileSize bytes,
286                                  GnomeVFSFileSize *bytes_written)
287 {
288         gint write_val;
289
290         do {
291                 write_val = write (connection->sock, buffer, bytes);
292         } while (write_val == -1 && errno == EINTR);
293
294         if (write_val == -1) {
295                 *bytes_written = 0;
296                 return gnome_vfs_result_from_errno ();
297         } else {
298                 *bytes_written = write_val;
299                 return GNOME_VFS_OK;
300         }
301 }
302
303 static GnomeVFSSocketImpl inet_connection_socket_impl = {
304         (GnomeVFSSocketReadFunc)gnome_vfs_inet_connection_read,
305         (GnomeVFSSocketWriteFunc)gnome_vfs_inet_connection_write,
306         (GnomeVFSSocketCloseFunc)gnome_vfs_inet_connection_close
307 };
308
309 /**
310  * gnome_vfs_inet_connection_to_socket:
311  * @connection: connection to convert to wrapper in a GnomeVFSSocket
312  *
313  * Wrapper @connection inside a standard GnomeVFSSocket for convenience.
314  *
315  * Return value: a newly created GnomeVFSSocket around @connection.
316  **/
317 GnomeVFSSocket *
318 gnome_vfs_inet_connection_to_socket (GnomeVFSInetConnection *connection)
319 {
320         return gnome_vfs_socket_new (&inet_connection_socket_impl, connection);
321 }
322
323 /**
324  * gnome_vfs_inet_connection_to_socket_buffer:
325  * @connection: connection to convert to wrapper in a GnomeVFSSocketBuffer
326  *
327  * Wrapper @connection inside a standard GnomeVFSSocketBuffer for convenience.
328  *
329  * Return value: a newly created GnomeVFSSocketBuffer around @connection.
330  **/
331 GnomeVFSSocketBuffer *
332 gnome_vfs_inet_connection_to_socket_buffer (GnomeVFSInetConnection *connection)
333 {
334         GnomeVFSSocket *socket;
335         socket = gnome_vfs_inet_connection_to_socket (connection);
336         return gnome_vfs_socket_buffer_new (socket);
337 }