16 * License along with this program; if not, write to the |
16 * License along with this program; if not, write to the |
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
18 * Boston, MA 02111-1307, USA. |
18 * Boston, MA 02111-1307, USA. |
19 */ |
19 */ |
20 |
20 |
|
21 #include <config.h> |
|
22 |
|
23 #include <string.h> |
|
24 |
|
25 #include "lm-debug.h" |
|
26 #include "lm-internals.h" |
|
27 #include "lm-misc.h" |
|
28 #include "lm-ssl.h" |
|
29 #include "lm-ssl-internals.h" |
|
30 #include "lm-proxy.h" |
21 #include "lm-socket.h" |
31 #include "lm-socket.h" |
22 |
32 #include "lm-sock.h" |
23 #ifndef G_OS_WIN32 |
33 #include "lm-error.h" |
24 typedef int LmSock; |
34 |
25 #else /* G_OS_WIN32 */ |
35 #define IN_BUFFER_SIZE 1024 |
26 typedef SOCKET LmSock; |
36 #define MIN_PORT 1 |
27 #endif /* G_OS_WIN32 */ |
37 #define MAX_PORT 65536 |
28 |
|
29 /* FIXME: Integrate with the SSL stuff */ |
|
30 |
|
31 /* FIXME: IO Buffering both in/out here? */ |
|
32 |
38 |
33 struct _LmSocket { |
39 struct _LmSocket { |
34 LmSock sock; |
40 LmConnection *connection; |
35 GIOChannel *io_channel; |
41 GMainContext *context; |
36 guint io_watch; |
42 |
37 /* FIXME: Add the rest */ |
43 gchar *server; |
38 |
44 guint port; |
39 LmSSL *ssl; |
45 |
40 |
46 gboolean blocking; |
41 LmSocketFuncs funcs; |
47 |
42 |
48 LmSSL *ssl; |
43 LmSocketState state; |
49 LmProxy *proxy; |
44 |
50 |
45 gboolean is_blocking; |
51 GIOChannel *io_channel; |
46 |
52 GSource *watch_in; |
47 gint ref; |
53 GSource *watch_err; |
48 }; |
54 GSource *watch_hup; |
49 |
55 |
50 typedef void (* SocketDNSCallback) (LmSocket *socket, |
56 LmSocketT fd; |
51 SocketDNSData *data, |
57 |
52 gboolean success); |
58 GSource *watch_connect; |
53 |
59 |
54 typedef struct { |
60 gboolean cancel_open; |
55 /* Used when socket tries to connect */ |
61 |
56 struct addrinfo *resolved_addrs; |
62 GSource *watch_out; |
57 struct addrinfo *current_addr; |
63 GString *out_buf; |
58 |
64 |
59 /* -- Internal during DNS lookup -- */ |
65 LmConnectData *connect_data; |
60 LmSocket *socket; |
66 |
61 gchar *host; |
67 IncomingDataFunc func; |
62 SocketDNSCallback callback; |
68 gpointer user_data; |
63 } SocketDNSData; |
69 |
64 |
70 guint ref_count; |
65 static LmSocket * socket_create (void); |
71 }; |
66 static void socket_free (LmSocket *socket); |
72 |
67 static gboolean socket_channel_event (GIOChannel *source, |
73 static void socket_free (LmSocket *socket); |
68 GIOCondition condition, |
74 static void socket_do_connect (LmConnectData *connect_data); |
69 LmSocket *socket); |
75 static gboolean socket_connect_cb (GIOChannel *source, |
70 static void socket_dns_lookup (LmSocket *socket, |
76 GIOCondition condition, |
71 const gchar *host, |
77 LmConnectData *connect_data); |
72 SocketDNSCallback callback); |
78 static gboolean socket_in_event (GIOChannel *source, |
73 static SocketDNSData * socket_dns_data_new (LmSocket *socket, |
79 GIOCondition condition, |
74 const gchar *host, |
80 LmSocket *socket); |
75 SocketDNSCallback callbac); |
81 static gboolean socket_hup_event (GIOChannel *source, |
76 static void socket_dns_data_free (SocketDNSData *data); |
82 GIOCondition condition, |
77 static void socket_start_connect (LmSocket *socket, |
83 LmSocket *socket); |
78 SocketDNSData *data, |
84 static gboolean |
79 gboolean success); |
85 socket_buffered_write_cb (GIOChannel *source, |
80 |
86 GIOCondition condition, |
81 static LmSocket * |
87 LmSocket *socket); |
82 socket_create (void) |
|
83 { |
|
84 LmSocket *socket; |
|
85 |
|
86 socket = g_new0 (LmSocket, 1); |
|
87 socket->ref_count = 1; |
|
88 socket->is_blocking = FALSE; |
|
89 socket->state = LM_SOCKET_STATE_CLOSED; |
|
90 socket->ssl = NULL; |
|
91 |
|
92 return socket; |
|
93 } |
|
94 |
88 |
95 static void |
89 static void |
96 socket_free (LmSocket *socket) |
90 socket_free (LmSocket *socket) |
97 { |
91 { |
98 /* FIXME: Free up the rest of the memory */ |
92 g_free (socket->server); |
99 |
|
100 if (socket->io_channel) { |
|
101 if (socket->io_watch != 0) { |
|
102 g_source_destroy (g_main_context_find_source_by_id (socket->context), |
|
103 socket->io_watch); |
|
104 socket->io_watch = 0; |
|
105 } |
|
106 |
|
107 g_io_channel_unref (socket->io_channel); |
|
108 socket->io_channel = NULL; |
|
109 |
|
110 socket->fd = -1; |
|
111 } |
|
112 |
93 |
113 if (socket->ssl) { |
94 if (socket->ssl) { |
114 _lm_ssl_unref (socket->ssl); |
95 lm_ssl_unref (socket->ssl); |
115 } |
96 } |
116 |
97 |
117 g_free (socket->host); |
98 if (socket->proxy) { |
|
99 lm_proxy_unref (socket->proxy); |
|
100 } |
|
101 |
|
102 if (socket->out_buf) { |
|
103 g_string_free (socket->out_buf, TRUE); |
|
104 } |
|
105 |
118 g_free (socket); |
106 g_free (socket); |
119 } |
107 } |
120 |
108 |
|
109 gint |
|
110 lm_socket_do_write (LmSocket *socket, |
|
111 const gchar *buf, |
|
112 gint len) |
|
113 { |
|
114 gint b_written; |
|
115 |
|
116 if (socket->ssl) { |
|
117 b_written = _lm_ssl_send (socket->ssl, buf, len); |
|
118 } else { |
|
119 GIOStatus io_status = G_IO_STATUS_AGAIN; |
|
120 gsize bytes_written; |
|
121 |
|
122 while (io_status == G_IO_STATUS_AGAIN) { |
|
123 io_status = g_io_channel_write_chars (socket->io_channel, |
|
124 buf, len, |
|
125 &bytes_written, |
|
126 NULL); |
|
127 } |
|
128 |
|
129 b_written = bytes_written; |
|
130 |
|
131 if (io_status != G_IO_STATUS_NORMAL) { |
|
132 b_written = -1; |
|
133 } |
|
134 } |
|
135 |
|
136 return b_written; |
|
137 } |
|
138 |
121 static gboolean |
139 static gboolean |
122 socket_channel_event (GIOChannel *source, |
140 socket_in_event (GIOChannel *source, |
|
141 GIOCondition condition, |
|
142 LmSocket *socket) |
|
143 { |
|
144 gchar buf[IN_BUFFER_SIZE]; |
|
145 gsize bytes_read; |
|
146 GIOStatus status; |
|
147 |
|
148 if (!socket->io_channel) { |
|
149 return FALSE; |
|
150 } |
|
151 |
|
152 if (socket->ssl) { |
|
153 status = _lm_ssl_read (socket->ssl, |
|
154 buf, IN_BUFFER_SIZE - 1, &bytes_read); |
|
155 } else { |
|
156 status = g_io_channel_read_chars (socket->io_channel, |
|
157 buf, IN_BUFFER_SIZE - 1, |
|
158 &bytes_read, |
|
159 NULL); |
|
160 } |
|
161 |
|
162 if (status != G_IO_STATUS_NORMAL || bytes_read < 0) { |
|
163 gint reason; |
|
164 |
|
165 switch (status) { |
|
166 case G_IO_STATUS_EOF: |
|
167 reason = LM_DISCONNECT_REASON_HUP; |
|
168 break; |
|
169 case G_IO_STATUS_AGAIN: |
|
170 return TRUE; |
|
171 break; |
|
172 case G_IO_STATUS_ERROR: |
|
173 reason = LM_DISCONNECT_REASON_ERROR; |
|
174 break; |
|
175 default: |
|
176 reason = LM_DISCONNECT_REASON_UNKNOWN; |
|
177 } |
|
178 |
|
179 _lm_connection_do_close (socket->connection); |
|
180 _lm_connection_signal_disconnect (socket->connection, reason); |
|
181 |
|
182 return FALSE; |
|
183 } |
|
184 |
|
185 buf[bytes_read] = '\0'; |
|
186 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, "\nRECV [%d]:\n", |
|
187 (int)bytes_read); |
|
188 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
189 "-----------------------------------\n"); |
|
190 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, "'%s'\n", buf); |
|
191 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
192 "-----------------------------------\n"); |
|
193 |
|
194 lm_verbose ("Read: %d chars\n", (int)bytes_read); |
|
195 |
|
196 (socket->func) (socket, buf, socket->user_data); |
|
197 |
|
198 return TRUE; |
|
199 } |
|
200 |
|
201 |
|
202 |
|
203 static gboolean |
|
204 socket_hup_event (GIOChannel *source, |
123 GIOCondition condition, |
205 GIOCondition condition, |
124 LmSocket *socket) |
206 LmSocket *socket) |
125 { |
207 { |
126 if (condition & G_IO_IN) { |
208 lm_verbose ("HUP event: %d->'%s'\n", |
127 socket_signal_read_available (); |
209 condition, lm_misc_io_condition_to_str (condition)); |
128 } |
210 |
129 if (condition & G_IO_OUT) { |
211 if (!socket->io_channel) { |
130 socket_attempt_write (); |
212 return FALSE; |
131 } |
213 } |
132 if (condition & G_IO_ERR || |
214 |
133 condition & G_IO_HUP) { |
215 _lm_connection_do_close (socket->connection); |
134 socket_disconnected (); |
216 _lm_connection_signal_disconnect (socket->connection, |
135 } |
217 LM_DISCONNECT_REASON_HUP); |
|
218 |
|
219 return TRUE; |
|
220 } |
|
221 |
|
222 void |
|
223 _lm_socket_succeeded (LmConnectData *connect_data) |
|
224 { |
|
225 LmSocket *socket; |
|
226 |
|
227 socket = connect_data->socket; |
|
228 |
|
229 if (socket->watch_connect) { |
|
230 g_source_destroy (socket->watch_connect); |
|
231 socket->watch_connect = NULL; |
|
232 } |
|
233 |
|
234 /* Need some way to report error/success */ |
|
235 if (socket->cancel_open) { |
|
236 lm_verbose ("Cancelling connection...\n"); |
|
237 return; |
|
238 } |
|
239 |
|
240 socket->fd = connect_data->fd; |
|
241 socket->io_channel = connect_data->io_channel; |
|
242 |
|
243 freeaddrinfo (connect_data->resolved_addrs); |
|
244 socket->connect_data = NULL; |
|
245 g_free (connect_data); |
|
246 |
|
247 if (socket->ssl) { |
|
248 GError *error = NULL; |
|
249 |
|
250 lm_verbose ("Setting up SSL...\n"); |
|
251 |
|
252 #ifdef HAVE_GNUTLS |
|
253 /* GNU TLS requires the socket to be blocking */ |
|
254 _lm_sock_set_blocking (socket->fd, TRUE); |
|
255 #endif |
|
256 |
|
257 if (!_lm_ssl_begin (socket->ssl, socket->fd, |
|
258 socket->server, |
|
259 &error)) { |
|
260 lm_verbose ("Could not begin SSL\n"); |
|
261 |
|
262 if (error) { |
|
263 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
264 "%s\n", error->message); |
|
265 g_error_free (error); |
|
266 } |
|
267 |
|
268 _lm_sock_shutdown (socket->fd); |
|
269 _lm_sock_close (socket->fd); |
|
270 |
|
271 _lm_connection_do_close (socket->connection); |
|
272 |
|
273 return; |
|
274 } |
|
275 |
|
276 #ifdef HAVE_GNUTLS |
|
277 _lm_sock_set_blocking (socket->fd, FALSE); |
|
278 #endif |
|
279 } |
|
280 |
|
281 socket->watch_in = |
|
282 lm_misc_add_io_watch (socket->context, |
|
283 socket->io_channel, |
|
284 G_IO_IN, |
|
285 (GIOFunc) socket_in_event, |
|
286 socket); |
|
287 |
|
288 /* FIXME: if we add these, we don't get ANY |
|
289 * response from the server, this is to do with the way that |
|
290 * windows handles watches, see bug #331214. |
|
291 */ |
|
292 #ifndef G_OS_WIN32 |
|
293 socket->watch_err = |
|
294 lm_misc_add_io_watch (socket->context, |
|
295 socket->io_channel, |
|
296 G_IO_ERR, |
|
297 (GIOFunc) _lm_connection_error_event, |
|
298 socket); |
|
299 |
|
300 socket->watch_hup = |
|
301 lm_misc_add_io_watch (socket->context, |
|
302 socket->io_channel, |
|
303 G_IO_HUP, |
|
304 (GIOFunc) socket_hup_event, |
|
305 socket); |
|
306 #endif |
|
307 |
|
308 _lm_connection_socket_result (socket->connection, TRUE); |
|
309 } |
|
310 |
|
311 void |
|
312 _lm_socket_failed_with_error (LmConnectData *connect_data, int error) |
|
313 { |
|
314 LmSocket *socket; |
|
315 |
|
316 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
317 "Connection failed: %s (error %d)\n", |
|
318 _lm_sock_get_error_str (error), error); |
|
319 |
|
320 socket = connect_data->socket; |
|
321 |
|
322 connect_data->current_addr = connect_data->current_addr->ai_next; |
|
323 |
|
324 if (socket->watch_connect) { |
|
325 g_source_destroy (socket->watch_connect); |
|
326 socket->watch_connect = NULL; |
|
327 } |
|
328 |
|
329 if (connect_data->io_channel != NULL) { |
|
330 g_io_channel_unref (connect_data->io_channel); |
|
331 /* FIXME: need to check for last unref and close the socket */ |
|
332 } |
|
333 |
|
334 if (connect_data->current_addr == NULL) { |
|
335 _lm_connection_socket_result (socket->connection, FALSE); |
|
336 |
|
337 freeaddrinfo (connect_data->resolved_addrs); |
|
338 socket->connect_data = NULL; |
|
339 g_free (connect_data); |
|
340 } else { |
|
341 /* try to connect to the next host */ |
|
342 socket_do_connect (connect_data); |
|
343 } |
|
344 } |
|
345 |
|
346 void |
|
347 _lm_socket_failed (LmConnectData *connect_data) |
|
348 { |
|
349 _lm_socket_failed_with_error (connect_data, _lm_sock_get_last_error()); |
|
350 } |
|
351 |
|
352 static gboolean |
|
353 socket_connect_cb (GIOChannel *source, |
|
354 GIOCondition condition, |
|
355 LmConnectData *connect_data) |
|
356 { |
|
357 LmSocket *socket; |
|
358 struct addrinfo *addr; |
|
359 int err; |
|
360 socklen_t len; |
|
361 LmSocketT fd; |
|
362 |
|
363 socket = connect_data->socket; |
|
364 addr = connect_data->current_addr; |
|
365 fd = g_io_channel_unix_get_fd (source); |
|
366 |
|
367 if (condition == G_IO_ERR) { |
|
368 len = sizeof (err); |
|
369 _lm_sock_get_error (fd, &err, &len); |
|
370 if (!_lm_sock_is_blocking_error (err)) { |
|
371 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
372 "Connection failed.\n"); |
|
373 |
|
374 _lm_socket_failed_with_error (connect_data, err); |
|
375 |
|
376 socket->watch_connect = NULL; |
|
377 return FALSE; |
|
378 } |
|
379 } |
|
380 |
|
381 if (_lm_connection_async_connect_waiting (socket->connection)) { |
|
382 gint res; |
|
383 |
|
384 fd = g_io_channel_unix_get_fd (source); |
|
385 |
|
386 res = _lm_sock_connect (fd, addr->ai_addr, (int)addr->ai_addrlen); |
|
387 if (res < 0) { |
|
388 err = _lm_sock_get_last_error (); |
|
389 if (_lm_sock_is_blocking_success (err)) { |
|
390 _lm_connection_set_async_connect_waiting (socket->connection, FALSE); |
|
391 |
|
392 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
393 "Connection success (1).\n"); |
|
394 |
|
395 _lm_socket_succeeded (connect_data); |
|
396 } |
|
397 |
|
398 if (_lm_connection_async_connect_waiting (socket->connection) && |
|
399 !_lm_sock_is_blocking_error (err)) { |
|
400 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
401 "Connection failed.\n"); |
|
402 |
|
403 _lm_sock_close (connect_data->fd); |
|
404 _lm_socket_failed_with_error (connect_data, err); |
|
405 |
|
406 socket->watch_connect = NULL; |
|
407 return FALSE; |
|
408 } |
|
409 } |
|
410 } else { |
|
411 /* for blocking sockets, G_IO_OUT means we are connected */ |
|
412 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
413 "Connection success (2).\n"); |
|
414 |
|
415 _lm_socket_succeeded (connect_data); |
|
416 } |
|
417 return TRUE; |
136 } |
418 } |
137 |
419 |
138 static void |
420 static void |
139 socket_dns_lookup (LmSocket *socket, |
421 socket_do_connect (LmConnectData *connect_data) |
140 const gchar *host, |
422 { |
141 SocketDNSCallback callback); |
423 LmSocket *socket; |
142 { |
424 LmSocketT fd; |
143 SocketDNSData *data; |
425 int res, err; |
144 struct addrinfo req; |
|
145 struct addrinfo ans; |
|
146 int err; |
|
147 |
|
148 /* FIXME: This should not be synchronous */ |
|
149 data = socket_dns_data_new (socket, host, callback); |
|
150 |
|
151 memset (&req, 0, sizeof (req)); |
|
152 req.ai_family = AF_UNSPEC; |
|
153 req.ai_socktype = SOCK_STREAM; |
|
154 req.ai_protocol = IPPROTO_TCP; |
|
155 |
|
156 err = getaddrinfo (data->host, NULL, &req, &ans); |
|
157 if (err != 0) { |
|
158 /* FIXME: Handle error */ |
|
159 data->callback (data->socket, data, FALSE); |
|
160 } |
|
161 |
|
162 data->resolved_addrs = ans; |
|
163 data->current_addr = ans; |
|
164 |
|
165 data->callback (data->socket, data, TRUE); |
|
166 } |
|
167 |
|
168 static SocketDNSData * |
|
169 socket_dns_data_new (LmSocket *socket, |
|
170 const gchar *host, |
|
171 SocketDNSCallback callbac) |
|
172 { |
|
173 SocketDNSData *data; |
|
174 |
|
175 data = g_new0 (SocketDNSData, 1); |
|
176 |
|
177 data->socket = lm_socket_ref (socket); |
|
178 data->host = g_strdup (host); |
|
179 data->callback = callback; |
|
180 |
|
181 data->resolved_addrs = data->current_addr = NULL; |
|
182 |
|
183 return data; |
|
184 } |
|
185 |
|
186 static void |
|
187 socket_dns_data_free (SocketDNSData *data) |
|
188 { |
|
189 lm_socket_unref (data->socket); |
|
190 g_free (data->host); |
|
191 |
|
192 if (data->resolved_addrs) { |
|
193 freeaddrinfo (data->resolved_addrs);; |
|
194 deta->resolved_addrs = NULL; |
|
195 } |
|
196 |
|
197 g_free (data); |
|
198 } |
|
199 |
|
200 static void |
|
201 socket_start_connect (LmSocket *socket, |
|
202 SocketDNSData *data, |
|
203 gboolean success) |
|
204 { |
|
205 struct addrinfo *addr; |
|
206 int port; |
426 int port; |
207 char name[NI_MAXHOST]; |
427 char name[NI_MAXHOST]; |
208 char portname[NI_MAXSERV]; |
428 char portname[NI_MAXSERV]; |
209 gint fd; |
429 struct addrinfo *addr; |
210 |
430 |
211 if (!success) { |
431 socket = connect_data->socket; |
212 socket_dns_data_free (data); |
432 addr = connect_data->current_addr; |
213 /* FIXME: Report error */ |
433 |
214 return; |
|
215 } |
|
216 |
|
217 addr = data->current_addr; |
|
218 |
|
219 if (socket->proxy) { |
434 if (socket->proxy) { |
220 port = htons (lm_proxy_get_port (socket->proxy)); |
435 port = htons (lm_proxy_get_port (socket->proxy)); |
221 } else { |
436 } else { |
222 port = htons (socket->port); |
437 port = htons (socket->port); |
223 } |
438 } |
224 |
439 |
225 ((struct sockaddr_in *) addr->ai_addr)->sin_port = port; |
440 ((struct sockaddr_in *) addr->ai_addr)->sin_port = port; |
226 |
441 |
227 res = getnameinfo (addr->ai_addr, |
442 res = getnameinfo (addr->ai_addr, |
228 (socklen_t) addr->ai_addrlen, |
443 (socklen_t)addr->ai_addrlen, |
229 name, sizeof (name), |
444 name, sizeof (name), |
230 portname, sizeof (portname), |
445 portname, sizeof (portname), |
231 NI_NUMERICHOST | NI_NUMERICSERV); |
446 NI_NUMERICHOST | NI_NUMERICSERV); |
232 |
447 |
233 if (res < 0) { |
448 if (res < 0) { |
234 /* FIXME: Report failure */ |
449 _lm_socket_failed (connect_data); |
235 return; |
450 return; |
236 } |
451 } |
237 |
452 |
|
453 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
454 "Trying %s port %s...\n", name, portname); |
|
455 |
238 fd = _lm_sock_makesocket (addr->ai_family, |
456 fd = _lm_sock_makesocket (addr->ai_family, |
239 addr->ai_socktype, |
457 addr->ai_socktype, |
240 addr->ai_protocol); |
458 addr->ai_protocol); |
241 |
459 |
242 if (!_LM_SOCK_VALID (fd)) { |
460 if (!_LM_SOCK_VALID (fd)) { |
243 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
461 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
244 "Failed making socket, error:%d...\n", |
462 "Failed making socket, error:%d...\n", |
245 _lm_sock_get_last_error ()); |
463 _lm_sock_get_last_error ()); |
246 |
464 |
247 /* FIXME: Report failure */ |
465 _lm_socket_failed (connect_data); |
|
466 |
248 return; |
467 return; |
249 } |
468 } |
250 |
469 |
251 /* Even though it says _unix_new(), it is supported by glib on |
470 /* Even though it says _unix_new(), it is supported by glib on |
252 * win32 because glib does some cool stuff to find out if it |
471 * win32 because glib does some cool stuff to find out if it |
253 * can treat it as a FD or a windows SOCKET. |
472 * can treat it as a FD or a windows SOCKET. |
254 */ |
473 */ |
255 socket->fd = fd; |
474 connect_data->fd = fd; |
256 socket->io_channel = g_io_channel_unix_new (fd); |
475 connect_data->io_channel = g_io_channel_unix_new (fd); |
257 |
476 |
258 g_io_channel_set_encoding (connect_data->io_channel, NULL, NULL); |
477 g_io_channel_set_encoding (connect_data->io_channel, NULL, NULL); |
259 g_io_channel_set_buffered (connect_data->io_channel, FALSE); |
478 g_io_channel_set_buffered (connect_data->io_channel, FALSE); |
260 |
479 |
261 _lm_sock_set_blocking (socket->fd, FALSE); |
480 _lm_sock_set_blocking (connect_data->fd, socket->blocking); |
262 |
481 |
263 if (socket->proxy) { |
482 if (socket->proxy) { |
264 socket->io_watch_connect = |
483 socket->watch_connect = |
265 socket_add_watch (socket, |
484 lm_misc_add_io_watch (socket->context, |
266 socket->io_channel, |
485 connect_data->io_channel, |
267 G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP, |
486 G_IO_OUT|G_IO_ERR, |
268 (GIOFunc) _lm_proxy_connect_cb, |
487 (GIOFunc) _lm_proxy_connect_cb, |
269 socket); |
488 connect_data); |
270 } else { |
489 } else { |
271 socket->io_watch_connect = |
490 socket->watch_connect = |
272 socket_add_watch (socket, |
491 lm_misc_add_io_watch (socket->context, |
273 socket->io_channel, |
492 connect_data->io_channel, |
274 G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP, |
493 G_IO_OUT|G_IO_ERR, |
275 (GIOFunc) socket_connect_cb, |
494 (GIOFunc) socket_connect_cb, |
276 socket); |
495 connect_data); |
277 } |
496 } |
278 |
497 |
279 /* FIXME: Continue and connect */ |
498 _lm_connection_set_async_connect_waiting (socket->connection, !socket->blocking); |
|
499 |
|
500 res = _lm_sock_connect (connect_data->fd, |
|
501 addr->ai_addr, (int)addr->ai_addrlen); |
|
502 if (res < 0) { |
|
503 err = _lm_sock_get_last_error (); |
|
504 if (!_lm_sock_is_blocking_error (err)) { |
|
505 _lm_sock_close (connect_data->fd); |
|
506 _lm_socket_failed_with_error (connect_data, err); |
|
507 |
|
508 return; |
|
509 } |
|
510 } |
|
511 } |
|
512 |
|
513 gboolean |
|
514 lm_socket_output_is_buffered (LmSocket *socket, |
|
515 const gchar *buffer, |
|
516 gint len) |
|
517 { |
|
518 if (socket->out_buf) { |
|
519 lm_verbose ("Appending %d bytes to output buffer\n", len); |
|
520 g_string_append_len (socket->out_buf, buffer, len); |
|
521 return TRUE; |
|
522 } |
|
523 |
|
524 return FALSE; |
|
525 } |
|
526 |
|
527 void |
|
528 lm_socket_setup_output_buffer (LmSocket *socket, |
|
529 const gchar *buffer, |
|
530 gint len) |
|
531 { |
|
532 lm_verbose ("OUTPUT BUFFER ENABLED\n"); |
|
533 |
|
534 socket->out_buf = g_string_new_len (buffer, len); |
|
535 |
|
536 socket->watch_out = |
|
537 lm_misc_add_io_watch (socket->context, |
|
538 socket->io_channel, |
|
539 G_IO_OUT, |
|
540 (GIOFunc) socket_buffered_write_cb, |
|
541 socket); |
|
542 } |
|
543 |
|
544 static gboolean |
|
545 socket_buffered_write_cb (GIOChannel *source, |
|
546 GIOCondition condition, |
|
547 LmSocket *socket) |
|
548 { |
|
549 gint b_written; |
|
550 GString *out_buf; |
|
551 /* FIXME: Do the writing */ |
|
552 |
|
553 out_buf = socket->out_buf; |
|
554 if (!out_buf) { |
|
555 /* Should not be possible */ |
|
556 return FALSE; |
|
557 } |
|
558 |
|
559 b_written = lm_socket_do_write (socket, out_buf->str, out_buf->len); |
|
560 |
|
561 if (b_written < 0) { |
|
562 _lm_connection_error_event (socket, |
|
563 G_IO_HUP, socket->connection); |
|
564 return FALSE; |
|
565 } |
|
566 |
|
567 g_string_erase (out_buf, 0, (gsize) b_written); |
|
568 if (out_buf->len == 0) { |
|
569 lm_verbose ("Output buffer is empty, going back to normal output\n"); |
|
570 |
|
571 if (socket->watch_out) { |
|
572 g_source_destroy (socket->watch_out); |
|
573 socket->watch_out = NULL; |
|
574 } |
|
575 |
|
576 g_string_free (out_buf, TRUE); |
|
577 socket->out_buf = NULL; |
|
578 return FALSE; |
|
579 } |
|
580 |
|
581 return TRUE; |
280 } |
582 } |
281 |
583 |
282 LmSocket * |
584 LmSocket * |
283 lm_socket_new (LmSocketFuncs funcs, const gchar *host, guint port) |
585 lm_socket_create (GMainContext *context, |
284 { |
586 IncomingDataFunc func, |
285 LmSocket *socket; |
587 gpointer user_data, |
286 |
588 LmConnection *connection, |
287 socket = socket_create (); |
589 gboolean blocking, |
288 |
590 const gchar *server, |
289 socket->funcs = funcs; |
591 guint port, |
290 socket->host = g_strdup (host); |
592 LmSSL *ssl, |
|
593 LmProxy *proxy, |
|
594 GError **error) |
|
595 { |
|
596 LmSocket *socket; |
|
597 struct addrinfo req; |
|
598 struct addrinfo *ans; |
|
599 LmConnectData *data; |
|
600 |
|
601 g_return_val_if_fail (server != NULL, NULL); |
|
602 g_return_val_if_fail ((port >= MIN_PORT && port <= MAX_PORT), NULL); |
|
603 g_return_val_if_fail (func != NULL, NULL); |
|
604 |
|
605 socket = g_new0 (LmSocket, 1); |
|
606 |
|
607 memset (&req, 0, sizeof(req)); |
|
608 |
|
609 req.ai_family = AF_UNSPEC; |
|
610 req.ai_socktype = SOCK_STREAM; |
|
611 req.ai_protocol = IPPROTO_TCP; |
|
612 |
|
613 socket->ref_count = 1; |
|
614 |
|
615 socket->connection = connection; |
|
616 socket->server = g_strdup (server); |
291 socket->port = port; |
617 socket->port = port; |
292 socket->is_blocking = FALSE; |
618 socket->cancel_open = FALSE; |
|
619 socket->ssl = NULL; |
|
620 socket->proxy = NULL; |
|
621 socket->blocking = blocking; |
|
622 socket->func = func; |
|
623 socket->user_data = user_data; |
|
624 |
|
625 if (context) { |
|
626 socket->context = g_main_context_ref (context); |
|
627 } |
|
628 |
|
629 if (proxy) { |
|
630 int err; |
|
631 const gchar *proxy_server; |
|
632 |
|
633 socket->proxy = lm_proxy_ref (proxy); |
|
634 |
|
635 proxy_server = lm_proxy_get_server (socket->proxy); |
|
636 |
|
637 /* Connect through proxy */ |
|
638 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
639 "Going to connect to proxy %s\n", proxy_server); |
|
640 |
|
641 err = getaddrinfo (proxy_server, NULL, &req, &ans); |
|
642 if (err != 0) { |
|
643 const char *str; |
|
644 |
|
645 str = _lm_sock_addrinfo_get_error_str (err); |
|
646 g_set_error (error, |
|
647 LM_ERROR, |
|
648 LM_ERROR_CONNECTION_FAILED, |
|
649 str); |
|
650 return NULL; |
|
651 } |
|
652 } else { |
|
653 int err; |
|
654 |
|
655 /* Connect directly */ |
|
656 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
|
657 "Going to connect to %s\n", |
|
658 socket->server); |
|
659 |
|
660 err = getaddrinfo (socket->server, |
|
661 NULL, &req, &ans); |
|
662 if (err != 0) { |
|
663 const char *str; |
|
664 |
|
665 str = _lm_sock_addrinfo_get_error_str (err); |
|
666 g_set_error (error, |
|
667 LM_ERROR, |
|
668 LM_ERROR_CONNECTION_FAILED, |
|
669 str); |
|
670 return NULL; |
|
671 } |
|
672 } |
|
673 |
|
674 if (ssl) { |
|
675 socket->ssl = lm_ssl_ref (ssl); |
|
676 _lm_ssl_initialize (socket->ssl); |
|
677 } |
|
678 |
|
679 /* Prepare and do the nonblocking connection */ |
|
680 data = g_new (LmConnectData, 1); |
|
681 |
|
682 data->socket = socket; |
|
683 data->connection = connection; |
|
684 data->resolved_addrs = ans; |
|
685 data->current_addr = ans; |
|
686 data->io_channel = NULL; |
|
687 data->fd = -1; |
|
688 |
|
689 socket->connect_data = data; |
|
690 |
|
691 socket_do_connect (data); |
293 |
692 |
294 return socket; |
693 return socket; |
295 } |
694 } |
296 |
695 |
297 void |
696 void |
298 lm_socket_open (LmSocket *socket) |
697 lm_socket_flush (LmSocket *socket) |
299 { |
698 { |
300 g_return_if_fail (socket != NULL); |
699 g_return_if_fail (socket != NULL); |
301 |
700 g_return_if_fail (socket->io_channel != NULL); |
302 socket_dns_lookup (socket, socket->host, socket_start_connect); |
701 |
303 } |
702 g_io_channel_flush (socket->io_channel, NULL); |
304 |
|
305 int |
|
306 lm_socket_get_fd (LmSocket *socket) |
|
307 { |
|
308 g_return_val_if_fail (socket != NULL, -1); |
|
309 |
|
310 return socket->fd; |
|
311 } |
|
312 |
|
313 gboolean |
|
314 lm_socket_get_is_blocking (LmSocket *socket) |
|
315 { |
|
316 return socket->is_blocking; |
|
317 } |
703 } |
318 |
704 |
319 void |
705 void |
320 lm_socket_set_is_blocking (LmSocket *socket, gboolean is_block) |
706 lm_socket_close (LmSocket *socket) |
321 { |
707 { |
322 int res; |
708 LmConnectData *data; |
323 |
709 |
324 g_return_if_fail (socket != NULL); |
710 if (socket->watch_connect) { |
325 |
711 g_source_destroy (socket->watch_connect); |
326 /* FIXME: Don't unset all flags */ |
712 socket->watch_connect = NULL; |
327 |
713 } |
328 #ifndef G_OS_WIN32 |
714 |
329 res = fcntl (socket->sock, F_SETFL, is_block ? 0 : O_NONBLOCK); |
715 data = socket->connect_data; |
330 #else /* G_OS_WIN32 */ |
716 if (data) { |
331 u_long mode = (is_block ? 0 : 1); |
717 freeaddrinfo (data->resolved_addrs); |
332 res = ioctlsocket (socket->sock, FIONBIO, &mode); |
718 socket->connect_data = NULL; |
333 #endif /* G_OS_WIN32 */ |
719 g_free (data); |
334 |
720 } |
335 if (res != 0) { |
721 |
336 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, |
722 if (socket->io_channel) { |
337 "Could not set socket to be %s\n", |
723 if (socket->watch_in) { |
338 block ? "blocking" : "non-blocking"); |
724 g_source_destroy (socket->watch_in); |
339 } |
725 socket->watch_in = NULL; |
340 |
726 } |
341 socket->is_blocking = is_block; |
727 |
342 } |
728 if (socket->watch_err) { |
343 |
729 g_source_destroy (socket->watch_err); |
344 int |
730 socket->watch_err = NULL; |
345 lm_socket_write (LmSocket *socket, |
731 } |
346 gsize size, |
732 |
347 gchar *buf, |
733 if (socket->watch_hup) { |
348 GError **error) |
734 g_source_destroy (socket->watch_hup); |
349 { |
735 socket->watch_hup = NULL; |
350 gint b_written; |
736 } |
351 |
737 |
352 g_return_val_if_fail (socket != NULL, -1); |
738 if (socket->watch_out) { |
353 |
739 g_source_destroy (socket->watch_out); |
354 if (socket->ssl) { |
740 socket->watch_out = NULL; |
355 b_written = _lm_ssl_send (socket->ssl, buf, len); |
741 } |
356 } else { |
742 |
357 GIOStatus io_status = G_IO_STATUS_AGAIN; |
743 g_io_channel_unref (socket->io_channel); |
358 gsize bytes_written = 0; |
744 socket->io_channel = NULL; |
359 |
745 |
360 while (io_status == G_IO_STATUS_AGAIN) { |
746 socket->fd = -1; |
361 io_status = g_io_channel_write_chars (socket->io_channel, |
747 } |
362 buf, size, |
|
363 &bytes_written, |
|
364 NULL); |
|
365 } |
|
366 |
|
367 b_written = bytes_written; |
|
368 |
|
369 if (io_status != G_IO_STATUS_NORMAL) { |
|
370 b_written = -1; |
|
371 } |
|
372 } |
|
373 |
|
374 return b_written; |
|
375 } |
|
376 |
|
377 int |
|
378 lm_socket_read (LmSocket *socket, |
|
379 gsize size, |
|
380 gchar *buf, |
|
381 GError **error) |
|
382 { |
|
383 gsize bytes_read = 0; |
|
384 GIOStatus status = G_IO_STATUS_AGAIN; |
|
385 |
|
386 g_return_val_if_fail (socket != NULL, -1); |
|
387 |
|
388 while (status == G_IO_STATUS_AGAIN) { |
|
389 if (socket->ssl) { |
|
390 status = _lm_ssl_read (socket->ssl, |
|
391 buf, size, &bytes_read); |
|
392 } else { |
|
393 status = g_io_channel_read_chars (socket->io_channel, |
|
394 buf, size, |
|
395 &bytes_read, |
|
396 NULL); |
|
397 } |
|
398 } |
|
399 |
|
400 if (status != G_IO_STATUS_NORMAL || bytes_read < 0) { |
|
401 /* FIXME: Set error */ |
|
402 |
|
403 return -1; |
|
404 } |
|
405 |
|
406 return bytes_read; |
|
407 } |
|
408 |
|
409 gboolean |
|
410 lm_socket_close (LmSocket *socket, GError **error) |
|
411 { |
|
412 #ifndef G_OS_WIN32 |
|
413 close (socket->sock); |
|
414 #else /* G_OS_WIN32 */ |
|
415 closesocket (socket->sock); |
|
416 #endif /* G_OS_WIN32 */ |
|
417 } |
748 } |
418 |
749 |
419 LmSocket * |
750 LmSocket * |
420 lm_socket_ref (LmSocket *socket) |
751 lm_socket_ref (LmSocket *socket) |
421 { |
752 { |
422 g_return_val_if_fail (socket != NULL, NULL); |
753 g_return_val_if_fail (socket != NULL, NULL); |
423 |
754 |
424 socket->ref++; |
755 socket->ref_count++; |
425 |
756 |
426 return socket; |
757 return socket; |
427 } |
758 } |
428 |
759 |
429 void |
760 void |
430 lm_socket_unref (LmSocket *socket) |
761 lm_socket_unref (LmSocket *socket) |
431 { |
762 { |
432 g_return_if_fail (socket != NULL); |
763 g_return_if_fail (socket != NULL); |
433 |
764 |
434 socket->ref--; |
765 socket->ref_count--; |
435 |
766 |
436 if (socket->ref <= 0) { |
767 if (socket->ref_count <= 0) { |
437 socket_free (socket); |
768 socket_free (socket); |
438 } |
769 } |
439 } |
770 } |
440 |
771 |