1 /* $Id$ */
2
3 /*
4 * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
5 * Robert J. Woźny <speedy@ziew.org>
6 * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
7 * Adam Wysocki <gophi@ekg.chmurka.net>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License Version
11 * 2.1 as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
21 * USA.
22 */
23
24 /**
25 * \file events.c
26 *
27 * \brief Obsługa zdarzeń
28 *
29 * \todo Poprawna obsługa gg_proxy_http_only
30 */
31
32 #include "strman.h"
33 #include "network.h"
34
35 #include "libgadu.h"
36 #include "protocol.h"
37 #include "internal.h"
38 #include "encoding.h"
39 #include "debug.h"
40 #include "session.h"
41 #include "resolver.h"
42 #include "config.h"
43
44 #include <errno.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <time.h>
48 #include <ctype.h>
49 #ifdef GG_CONFIG_HAVE_GNUTLS
50 # include <gnutls/gnutls.h>
51 # include <gnutls/x509.h>
52 #endif
53 #ifdef GG_CONFIG_HAVE_OPENSSL
54 # include <openssl/err.h>
55 # include <openssl/x509.h>
56 # include <openssl/rand.h>
57 #endif
58
59 /**
60 * Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
61 *
62 * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci
63 * strukturę \c gg_event.
64 *
65 * \param e Struktura zdarzenia
66 *
67 * \ingroup events
68 */
gg_event_free(struct gg_event * e)69 void gg_event_free(struct gg_event *e)
70 {
71 gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
72
73 if (!e)
74 return;
75
76 switch (e->type) {
77 case GG_EVENT_MSG:
78 case GG_EVENT_MULTILOGON_MSG:
79 free(e->event.msg.message);
80 free(e->event.msg.formats);
81 free(e->event.msg.recipients);
82 free(e->event.msg.xhtml_message);
83 break;
84
85 case GG_EVENT_NOTIFY:
86 free(e->event.notify);
87 break;
88
89 case GG_EVENT_NOTIFY60:
90 {
91 int i;
92
93 for (i = 0; e->event.notify60[i].uin; i++)
94 free(e->event.notify60[i].descr);
95
96 free(e->event.notify60);
97
98 break;
99 }
100
101 case GG_EVENT_STATUS60:
102 free(e->event.status60.descr);
103 break;
104
105 case GG_EVENT_STATUS:
106 free(e->event.status.descr);
107 break;
108
109 case GG_EVENT_NOTIFY_DESCR:
110 free(e->event.notify_descr.notify);
111 free(e->event.notify_descr.descr);
112 break;
113
114 case GG_EVENT_DCC_VOICE_DATA:
115 free(e->event.dcc_voice_data.data);
116 break;
117
118 case GG_EVENT_PUBDIR50_SEARCH_REPLY:
119 case GG_EVENT_PUBDIR50_READ:
120 case GG_EVENT_PUBDIR50_WRITE:
121 gg_pubdir50_free(e->event.pubdir50);
122 break;
123
124 case GG_EVENT_USERLIST:
125 free(e->event.userlist.reply);
126 break;
127
128 case GG_EVENT_IMAGE_REPLY:
129 free(e->event.image_reply.filename);
130 free(e->event.image_reply.image);
131 break;
132
133 case GG_EVENT_XML_EVENT:
134 free(e->event.xml_event.data);
135 break;
136
137 case GG_EVENT_JSON_EVENT:
138 free(e->event.json_event.data);
139 free(e->event.json_event.type);
140 break;
141
142 case GG_EVENT_USER_DATA:
143 {
144 unsigned int i, j;
145
146 for (i = 0; i < e->event.user_data.user_count; i++) {
147 for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
148 free(e->event.user_data.users[i].attrs[j].key);
149 free(e->event.user_data.users[i].attrs[j].value);
150 }
151
152 free(e->event.user_data.users[i].attrs);
153 }
154
155 free(e->event.user_data.users);
156
157 break;
158 }
159
160 case GG_EVENT_MULTILOGON_INFO:
161 {
162 int i;
163
164 for (i = 0; i < e->event.multilogon_info.count; i++)
165 free(e->event.multilogon_info.sessions[i].name);
166
167 free(e->event.multilogon_info.sessions);
168
169 break;
170 }
171
172 case GG_EVENT_USERLIST100_REPLY:
173 free(e->event.userlist100_reply.reply);
174 break;
175
176 case GG_EVENT_IMTOKEN:
177 free(e->event.imtoken.imtoken);
178 break;
179
180 case GG_EVENT_CHAT_INFO:
181 free(e->event.chat_info.participants);
182 break;
183 }
184
185 free(e);
186 }
187
188 /** \cond internal */
189
190 /**
191 * \internal Usuwa obrazek z kolejki do wysłania.
192 *
193 * \param s Struktura sesji
194 * \param q Struktura obrazka
195 * \param freeq Flaga zwolnienia elementu kolejki
196 *
197 * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
198 */
gg_image_queue_remove(struct gg_session * s,struct gg_image_queue * q,int freeq)199 int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
200 {
201 if (!s || !q) {
202 errno = EFAULT;
203 return -1;
204 }
205
206 if (s->images == q)
207 s->images = q->next;
208 else {
209 struct gg_image_queue *qq;
210
211 for (qq = s->images; qq; qq = qq->next) {
212 if (qq->next == q) {
213 qq->next = q->next;
214 break;
215 }
216 }
217 }
218
219 if (freeq) {
220 free(q->image);
221 free(q->filename);
222 free(q);
223 }
224
225 return 0;
226 }
227
228 /** \endcond */
229
230 /**
231 * \internal Inicjalizuje struktury SSL.
232 *
233 * \param gs Struktura sesji
234 *
235 * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
236 */
gg_session_init_ssl(struct gg_session * gs)237 int gg_session_init_ssl(struct gg_session *gs)
238 {
239 #ifdef GG_CONFIG_HAVE_GNUTLS
240 gg_session_gnutls_t *tmp;
241
242 tmp = (gg_session_gnutls_t*) gs->ssl;
243
244 if (tmp == NULL) {
245 tmp = malloc(sizeof(gg_session_gnutls_t));
246
247 if (tmp == NULL) {
248 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() out of memory for GnuTLS session\n");
249 return -1;
250 }
251
252 memset(tmp, 0, sizeof(gg_session_gnutls_t));
253
254 gs->ssl = tmp;
255
256 gnutls_global_init();
257 gnutls_certificate_allocate_credentials(&tmp->xcred);
258 #ifdef GG_CONFIG_SSL_SYSTEM_TRUST
259 #ifdef HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST
260 gnutls_certificate_set_x509_system_trust(tmp->xcred);
261 #else
262 gnutls_certificate_set_x509_trust_file(tmp->xcred,
263 GG_CONFIG_GNUTLS_SYSTEM_TRUST_STORE,
264 GNUTLS_X509_FMT_PEM);
265 #endif
266 #endif
267 } else {
268 gnutls_deinit(tmp->session);
269 }
270
271 gnutls_init(&tmp->session, GNUTLS_CLIENT);
272 gnutls_set_default_priority(tmp->session);
273 gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred);
274 gnutls_transport_set_ptr(tmp->session, (gnutls_transport_ptr_t) (intptr_t) gs->fd);
275 #endif
276
277 #ifdef GG_CONFIG_HAVE_OPENSSL
278 char buf[1024];
279
280 OpenSSL_add_ssl_algorithms();
281
282 if (!RAND_status()) {
283 char rdata[1024];
284 struct {
285 time_t time;
286 void *ptr;
287 } rstruct;
288
289 time(&rstruct.time);
290 rstruct.ptr = (void *) &rstruct;
291
292 RAND_seed((void *) rdata, sizeof(rdata));
293 RAND_seed((void *) &rstruct, sizeof(rstruct));
294 }
295
296 if (gs->ssl_ctx == NULL) {
297 gs->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
298
299 if (gs->ssl_ctx == NULL) {
300 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
301 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() SSL_CTX_new() failed: %s\n", buf);
302 return -1;
303 }
304
305 SSL_CTX_set_verify(gs->ssl_ctx, SSL_VERIFY_NONE, NULL);
306 #ifdef GG_CONFIG_SSL_SYSTEM_TRUST
307 SSL_CTX_set_default_verify_paths(gs->ssl_ctx);
308 #endif
309 }
310
311 if (gs->ssl != NULL)
312 SSL_free(gs->ssl);
313
314 gs->ssl = SSL_new(gs->ssl_ctx);
315
316 if (gs->ssl == NULL) {
317 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
318 gg_debug(GG_DEBUG_MISC, "// gg_session_connect() SSL_new() failed: %s\n", buf);
319 return -1;
320 }
321
322 SSL_set_fd(gs->ssl, gs->fd);
323 #endif
324
325 return 0;
326 }
327
328 /**
329 * \internal Funkcja próbuje wysłać dane zakolejkowane do wysyłki.
330 *
331 * \param sess Struktura sesji
332 *
333 * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
334 */
gg_send_queued_data(struct gg_session * sess)335 static int gg_send_queued_data(struct gg_session *sess)
336 {
337 int res;
338
339 if (sess->send_buf == NULL || sess->send_left == 0)
340 return 0;
341
342 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
343
344 res = send(sess->fd, sess->send_buf, sess->send_left, 0);
345
346 if (res == -1) {
347 if (errno == EAGAIN || errno == EINTR) {
348 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
349 " non-critical send error (errno=%d, %s)\n",
350 errno, strerror(errno));
351
352 return 0;
353 }
354
355 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() send() "
356 "failed (errno=%d, %s)\n", errno, strerror(errno));
357
358 return -1;
359 }
360
361 if (res == sess->send_left) {
362 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
363 free(sess->send_buf);
364 sess->send_buf = NULL;
365 sess->send_left = 0;
366 } else if (res > 0) {
367 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d"
368 " bytes of queued data, %d bytes left\n",
369 res, sess->send_left - res);
370
371 memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
372 sess->send_left -= res;
373 }
374
375 return 0;
376 }
377
378 /**
379 * \internal Sprawdza wynik połączenia asynchronicznego.
380 * \param gs Struktura sesji
381 * \param res_ptr Wskaźnik na kod błędu
382 * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
383 */
gg_async_connect_failed(struct gg_session * gs,int * res_ptr)384 static int gg_async_connect_failed(struct gg_session *gs, int *res_ptr)
385 {
386 int res = 0;
387 socklen_t res_size = sizeof(res);
388
389 if (!gs->async)
390 return 0;
391
392 if (gs->timeout == 0) {
393 *res_ptr = ETIMEDOUT;
394 return 1;
395 }
396
397 if (getsockopt(gs->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) == -1) {
398 *res_ptr = errno;
399 return 1;
400 }
401
402 if (res != 0) {
403 *res_ptr = res;
404 return 1;
405 }
406
407 *res_ptr = 0;
408
409 return 0;
410 }
411
412 typedef enum
413 {
414 GG_ACTION_WAIT,
415 GG_ACTION_NEXT,
416 GG_ACTION_FAIL
417 } gg_action_t;
418
419 typedef gg_action_t (*gg_state_handler_t)(struct gg_session *gs,
420 struct gg_event *ge, enum gg_state_t next_state,
421 enum gg_state_t alt_state, enum gg_state_t alt2_state);
422
423 typedef struct
424 {
425 enum gg_state_t state;
426 gg_state_handler_t handler;
427 enum gg_state_t next_state;
428 enum gg_state_t alt_state;
429 enum gg_state_t alt2_state;
430 } gg_state_transition_t;
431
432 /* zwraca:
433 * -1 w przypadku błędu
434 * 0 jeżeli nie ma ustawionego specjalnego managera gniazdek
435 * 1 w przypadku powodzenia
436 */
gg_handle_resolve_custom(struct gg_session * sess,enum gg_state_t next_state)437 static int gg_handle_resolve_custom(struct gg_session *sess, enum gg_state_t next_state)
438 {
439 struct gg_session_private *p = sess->private_data;
440 int is_tls = 0;
441 int port;
442
443 if (p->socket_manager_type == GG_SOCKET_MANAGER_TYPE_INTERNAL)
444 return 0;
445
446 if (p->socket_manager.connect_cb == NULL) {
447 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
448 "// gg_handle_resolve_custom() socket_manager.connect "
449 "callback is empty\n");
450 return -1;
451 }
452
453 if (p->socket_handle != NULL) {
454 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
455 "// gg_handle_resolve_custom() socket_handle is not "
456 "NULL\n");
457 return -1;
458 }
459
460 port = sess->connect_port[sess->connect_index];
461 if (next_state == GG_STATE_SEND_HUB)
462 port = GG_APPMSG_PORT;
463
464 if (sess->ssl_flag != GG_SSL_DISABLED &&
465 next_state == GG_STATE_READING_KEY)
466 {
467 /* XXX: w tej chwili nie ma możliwości łączenia się do HUBa po
468 * SSL, ale może będzie w przyszłości */
469 is_tls = 1;
470 }
471
472 if (is_tls && p->socket_manager_type == GG_SOCKET_MANAGER_TYPE_TCP) {
473 is_tls = 0;
474 next_state = GG_STATE_TLS_NEGOTIATION;
475 }
476
477 if (port <= 0) {
478 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
479 "// gg_handle_resolve_custom() port <= 0\n");
480 return -1;
481 }
482
483 p->socket_failure = 0;
484 p->socket_next_state = next_state;
485 p->socket_handle = p->socket_manager.connect_cb(
486 p->socket_manager.cb_data, sess->resolver_host, port, is_tls,
487 sess->async, sess);
488
489 if (p->socket_failure != 0) {
490 if (p->socket_handle != NULL) {
491 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_WARNING,
492 "// gg_handle_resolve_custom() handle should be"
493 " empty on error\n");
494 }
495 return -1;
496 }
497
498 if (p->socket_handle == NULL) {
499 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
500 "// gg_handle_resolve_custom() returned empty "
501 "handle\n");
502 return -1;
503 }
504
505 return 1;
506 }
507
gg_handle_resolve_sync(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)508 static gg_action_t gg_handle_resolve_sync(struct gg_session *sess,
509 struct gg_event *e, enum gg_state_t next_state,
510 enum gg_state_t alt_state, enum gg_state_t alt2_state)
511 {
512 struct in_addr addr;
513 int res;
514
515 res = gg_handle_resolve_custom(sess, alt_state);
516 if (res == 1)
517 return GG_ACTION_NEXT;
518 else if (res == -1)
519 return GG_ACTION_FAIL;
520
521 addr.s_addr = inet_addr(sess->resolver_host);
522
523 if (addr.s_addr == INADDR_NONE) {
524 struct in_addr *addr_list = NULL;
525 unsigned int addr_count;
526
527 if (gg_gethostbyname_real(sess->resolver_host, &addr_list, &addr_count, 0) == -1) {
528 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
529 " host %s not found\n", sess->resolver_host);
530 e->event.failure = GG_FAILURE_RESOLVING;
531 free(addr_list);
532 return GG_ACTION_FAIL;
533 }
534
535 sess->resolver_result = addr_list;
536 sess->resolver_count = addr_count;
537 sess->resolver_index = 0;
538 } else {
539 sess->resolver_result = malloc(sizeof(struct in_addr));
540
541 if (sess->resolver_result == NULL) {
542 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
543 return GG_ACTION_FAIL;
544 }
545
546 sess->resolver_result[0].s_addr = addr.s_addr;
547 sess->resolver_count = 1;
548 sess->resolver_index = 0;
549 }
550
551 sess->state = next_state;
552
553 return GG_ACTION_NEXT;
554 }
555
gg_handle_resolve_async(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)556 static gg_action_t gg_handle_resolve_async(struct gg_session *sess,
557 struct gg_event *e, enum gg_state_t next_state,
558 enum gg_state_t alt_state, enum gg_state_t alt2_state)
559 {
560 int res;
561
562 res = gg_handle_resolve_custom(sess, alt_state);
563 if (res == 1)
564 return GG_ACTION_WAIT;
565 else if (res == -1)
566 return GG_ACTION_FAIL;
567
568 if (sess->resolver_start(&sess->fd, &sess->resolver, sess->resolver_host) == -1) {
569 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
570 "resolving failed (errno=%d, %s)\n",
571 errno, strerror(errno));
572 e->event.failure = GG_FAILURE_RESOLVING;
573 return GG_ACTION_FAIL;
574 }
575
576 sess->state = next_state;
577 sess->check = GG_CHECK_READ;
578 sess->timeout = GG_DEFAULT_TIMEOUT;
579
580 return GG_ACTION_WAIT;
581 }
582
gg_handle_resolving(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)583 static gg_action_t gg_handle_resolving(struct gg_session *sess,
584 struct gg_event *e, enum gg_state_t next_state,
585 enum gg_state_t alt_state, enum gg_state_t alt2_state)
586 {
587 char buf[256];
588 int count = -1;
589 int res;
590 unsigned int i;
591 struct in_addr *addrs;
592
593 res = gg_resolver_recv(sess->fd, buf, sizeof(buf));
594
595 if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
596 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
597 "non-critical error (errno=%d, %s)\n",
598 errno, strerror(errno));
599 return GG_ACTION_WAIT;
600 }
601
602 sess->resolver_cleanup(&sess->resolver, 0);
603
604 if (res == -1) {
605 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() read "
606 "error (errno=%d, %s)\n", errno, strerror(errno));
607 e->event.failure = GG_FAILURE_RESOLVING;
608 return GG_ACTION_FAIL;
609 }
610
611 if (res > 0) {
612 char *tmp;
613
614 tmp = realloc(sess->recv_buf, sess->recv_done + res);
615
616 if (tmp == NULL)
617 return GG_ACTION_FAIL;
618
619 sess->recv_buf = tmp;
620 memcpy(sess->recv_buf + sess->recv_done, buf, res);
621 sess->recv_done += res;
622 }
623
624 /* Sprawdź, czy mamy listę zakończoną INADDR_NONE */
625
626 addrs = (struct in_addr *)(void *)sess->recv_buf;
627
628 for (i = 0; i < sess->recv_done / sizeof(struct in_addr); i++) {
629 if (addrs[i].s_addr == INADDR_NONE) {
630 count = i;
631 break;
632 }
633 }
634
635 /* Nie znaleziono hosta */
636
637 if (count == 0) {
638 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() host not found\n");
639 e->event.failure = GG_FAILURE_RESOLVING;
640 return GG_ACTION_FAIL;
641 }
642
643 /* Nie mamy pełnej listy, ale połączenie zerwane */
644
645 if (res == 0 && count == -1) {
646 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection broken\n");
647 e->event.failure = GG_FAILURE_RESOLVING;
648 return GG_ACTION_FAIL;
649 }
650
651 /* Nie mamy pełnej listy, normalna sytuacja */
652
653 if (count == -1)
654 return GG_ACTION_WAIT;
655
656 #ifndef GG_DEBUG_DISABLE
657 if ((gg_debug_level & GG_DEBUG_DUMP) && (count > 0)) {
658 char *list;
659 size_t len;
660
661 len = 0;
662
663 for (i = 0; i < (unsigned int) count; i++) {
664 if (i > 0)
665 len += 2;
666
667 len += strlen(inet_ntoa(addrs[i]));
668 }
669
670 list = malloc(len + 1);
671
672 if (list == NULL)
673 return GG_ACTION_FAIL;
674
675 list[0] = 0;
676
677 for (i = 0; i < (unsigned int) count; i++) {
678 if (i > 0)
679 strcat(list, ", ");
680
681 strcat(list, inet_ntoa(addrs[i]));
682 }
683
684 gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() resolved: %s\n", list);
685
686 free(list);
687 }
688 #endif
689
690 gg_close(sess);
691
692 sess->state = next_state;
693 sess->resolver_result = addrs;
694 sess->resolver_count = count;
695 sess->resolver_index = 0;
696 sess->recv_buf = NULL;
697 sess->recv_done = 0;
698
699 return GG_ACTION_NEXT;
700 }
701
gg_handle_connect(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)702 static gg_action_t gg_handle_connect(struct gg_session *sess,
703 struct gg_event *e, enum gg_state_t next_state,
704 enum gg_state_t alt_state, enum gg_state_t alt2_state)
705 {
706 struct in_addr addr;
707 int port;
708
709 if (sess->resolver_index >= sess->resolver_count) {
710 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of addresses to connect to\n");
711 e->event.failure = GG_FAILURE_CONNECTING;
712 return GG_ACTION_FAIL;
713 }
714
715 addr = sess->resolver_result[sess->resolver_index];
716
717 if (sess->state == GG_STATE_CONNECT_HUB) {
718 sess->hub_addr = addr.s_addr;
719 port = GG_APPMSG_PORT;
720 } else {
721 sess->proxy_addr = addr.s_addr;
722 port = sess->proxy_port;
723 }
724
725 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connecting to %s:%d\n", inet_ntoa(addr), port);
726
727 sess->fd = gg_connect(&addr, port, sess->async);
728
729 if (sess->fd == -1) {
730 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
731 "connection failed (errno=%d, %s)\n",
732 errno, strerror(errno));
733 sess->resolver_index++;
734 return GG_ACTION_NEXT;
735 }
736
737 sess->state = next_state;
738 sess->check = GG_CHECK_WRITE;
739 sess->timeout = GG_DEFAULT_TIMEOUT;
740 sess->soft_timeout = 1;
741
742 return GG_ACTION_WAIT;
743 }
744
gg_handle_connecting(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)745 static gg_action_t gg_handle_connecting(struct gg_session *sess,
746 struct gg_event *e, enum gg_state_t next_state,
747 enum gg_state_t alt_state, enum gg_state_t alt2_state)
748 {
749 int res;
750
751 sess->soft_timeout = 0;
752
753 if (gg_async_connect_failed(sess, &res)) {
754 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
755 "connection failed (errno=%d, %s)\n",
756 res, strerror(res));
757 gg_close(sess);
758 sess->resolver_index++;
759 sess->state = alt_state;
760 } else {
761 /* Z proxy zwykle łączymy się dwa razy, więc nie zwalniamy
762 * adresów IP po pierwszym połączeniu. */
763 if (sess->state != GG_STATE_CONNECTING_PROXY_HUB) {
764 free(sess->resolver_result);
765 sess->resolver_result = NULL;
766 }
767
768 sess->state = next_state;
769 }
770
771 return GG_ACTION_NEXT;
772 }
773
gg_handle_connect_gg(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)774 static gg_action_t gg_handle_connect_gg(struct gg_session *sess,
775 struct gg_event *e, enum gg_state_t next_state,
776 enum gg_state_t alt_state, enum gg_state_t alt2_state)
777 {
778 struct in_addr addr;
779 uint16_t port;
780
781 gg_debug_session(sess, GG_DEBUG_MISC, "resolver_index=%d, "
782 "connect_index=%d, connect_port={%d,%d}\n",
783 sess->resolver_index, sess->connect_index,
784 sess->connect_port[0], sess->connect_port[1]);
785
786 if ((unsigned int) sess->connect_index >=
787 sizeof(sess->connect_port) / sizeof(sess->connect_port[0]) ||
788 sess->connect_port[sess->connect_index] == 0)
789 {
790 sess->connect_index = 0;
791 sess->resolver_index++;
792 if (sess->resolver_index >= sess->resolver_count) {
793 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of addresses to connect to\n");
794 e->event.failure = GG_FAILURE_CONNECTING;
795 return GG_ACTION_FAIL;
796 }
797 }
798
799 addr = sess->resolver_result[sess->resolver_index];
800 port = sess->connect_port[sess->connect_index];
801
802 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connecting to %s:%d\n", inet_ntoa(addr), port);
803
804 sess->server_addr = addr.s_addr;
805 sess->fd = gg_connect(&addr, port, sess->async);
806
807 if (sess->fd == -1) {
808 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
809 "connection failed (errno=%d, %s)\n",
810 errno, strerror(errno));
811 sess->connect_index++;
812 return GG_ACTION_NEXT;
813 }
814
815 sess->state = next_state;
816 sess->check = GG_CHECK_WRITE;
817 sess->timeout = GG_DEFAULT_TIMEOUT;
818 sess->soft_timeout = 1;
819
820 return GG_ACTION_WAIT;
821 }
822
gg_handle_connecting_gg(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)823 static gg_action_t gg_handle_connecting_gg(struct gg_session *sess,
824 struct gg_event *e, enum gg_state_t next_state,
825 enum gg_state_t alt_state, enum gg_state_t alt2_state)
826 {
827 int res;
828
829 sess->soft_timeout = 0;
830
831 /* jeśli wystąpił błąd podczas łączenia się... */
832 if (gg_async_connect_failed(sess, &res)) {
833 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
834 "connection failed (errno=%d, %s)\n",
835 res, strerror(res));
836 gg_close(sess);
837 sess->connect_index++;
838 sess->state = alt_state;
839 return GG_ACTION_NEXT;
840 }
841
842 free(sess->resolver_result);
843 sess->resolver_result = NULL;
844
845 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
846
847 if (sess->ssl_flag != GG_SSL_DISABLED) {
848 if (gg_session_init_ssl(sess) == -1) {
849 e->event.failure = GG_FAILURE_TLS;
850 return GG_ACTION_FAIL;
851 }
852
853 sess->state = alt2_state;
854 sess->check = GG_CHECK_WRITE;
855 sess->timeout = GG_DEFAULT_TIMEOUT;
856
857 return GG_ACTION_NEXT;
858 } else {
859 sess->state = next_state;
860 sess->check = GG_CHECK_READ;
861 sess->timeout = GG_DEFAULT_TIMEOUT;
862
863 return GG_ACTION_WAIT;
864 }
865 }
866
gg_handle_send_hub(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)867 static gg_action_t gg_handle_send_hub(struct gg_session *sess,
868 struct gg_event *e, enum gg_state_t next_state,
869 enum gg_state_t alt_state, enum gg_state_t alt2_state)
870 {
871 char *req, *client, *auth;
872 const char *host;
873 int res;
874 int proxy;
875 size_t req_len;
876
877 if (sess->client_version != NULL && isdigit(sess->client_version[0]))
878 client = gg_urlencode(sess->client_version);
879 else if (sess->protocol_version <= GG_PROTOCOL_VERSION_100)
880 client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION_100);
881 else /* sess->protocol_version >= GG_PROTOCOL_VERSION_110 */
882 client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION_110);
883
884 if (client == NULL) {
885 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
886 return GG_ACTION_FAIL;
887 }
888
889 if (sess->proxy_addr && sess->proxy_port) {
890 host = "http://" GG_APPMSG_HOST;
891 proxy = 1;
892 } else {
893 host = "";
894 proxy = 0;
895 }
896
897 auth = gg_proxy_auth();
898
899 if (sess->ssl_flag != GG_SSL_DISABLED) {
900 req = gg_saprintf
901 ("GET %s/appsvc/appmsg_ver10.asp?fmnumber=%u&fmt=2&"
902 "lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
903 "Connection: close\r\n"
904 "Host: " GG_APPMSG_HOST "\r\n"
905 "%s"
906 "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
907 } else {
908 req = gg_saprintf
909 ("GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
910 "Host: " GG_APPMSG_HOST "\r\n"
911 "%s"
912 "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
913 }
914
915 free(auth);
916 free(client);
917
918 if (req == NULL) {
919 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
920 e->event.failure = GG_FAILURE_PROXY;
921 return GG_ACTION_FAIL;
922 }
923
924 req_len = strlen(req);
925
926 gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// sending http query:\n%s", req);
927
928 res = send(sess->fd, req, req_len, 0);
929
930 free(req);
931
932 if (res == -1 && errno != EINTR && errno != EAGAIN) {
933 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
934 e->event.failure = (!proxy) ? GG_FAILURE_HUB : GG_FAILURE_PROXY;
935 return GG_ACTION_FAIL;
936 }
937
938 if ((size_t) res < req_len) {
939 sess->state = alt_state;
940 sess->check = GG_CHECK_WRITE;
941 sess->timeout = GG_DEFAULT_TIMEOUT;
942 } else {
943 sess->state = next_state;
944 sess->check = GG_CHECK_READ;
945 sess->timeout = GG_DEFAULT_TIMEOUT;
946 }
947
948 return GG_ACTION_WAIT;
949 }
950
gg_handle_sending_hub_proxy(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)951 static gg_action_t gg_handle_sending_hub_proxy(struct gg_session *sess,
952 struct gg_event *e, enum gg_state_t next_state,
953 enum gg_state_t alt_state, enum gg_state_t alt2_state)
954 {
955 if (gg_send_queued_data(sess) == -1) {
956 e->event.failure = GG_FAILURE_WRITING;
957 return GG_ACTION_FAIL;
958 }
959
960 if (sess->send_left > 0)
961 return GG_ACTION_WAIT;
962
963 sess->state = next_state;
964 sess->check = GG_CHECK_READ;
965 sess->timeout = GG_DEFAULT_TIMEOUT;
966
967 return GG_ACTION_WAIT;
968 }
969
gg_handle_reading_hub_proxy(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)970 static gg_action_t gg_handle_reading_hub_proxy(struct gg_session *sess,
971 struct gg_event *e, enum gg_state_t next_state,
972 enum gg_state_t alt_state, enum gg_state_t alt2_state)
973 {
974 char buf[1024], *tmp, host[129];
975 int port = GG_DEFAULT_PORT;
976 int reply;
977 const char *body;
978 struct in_addr addr;
979 int res;
980 char **host_white;
981 char *host_white_default[] = GG_DEFAULT_HOST_WHITE_LIST;
982
983 res = recv(sess->fd, buf, sizeof(buf), 0);
984
985 if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
986 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
987 "non-critical recv error (errno=%d, %s)\n",
988 errno, strerror(errno));
989 return GG_ACTION_WAIT;
990 }
991
992 if (res == -1) {
993 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() recv "
994 "error (errno=%d, %s)\n", errno, strerror(errno));
995 e->event.failure = GG_FAILURE_CONNECTING;
996 return GG_ACTION_FAIL;
997 }
998
999 if (res != 0) {
1000 tmp = realloc(sess->recv_buf, sess->recv_done + res + 1);
1001
1002 if (tmp == NULL) {
1003 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for http reply\n");
1004 return GG_ACTION_FAIL;
1005 }
1006
1007 sess->recv_buf = tmp;
1008 memcpy(sess->recv_buf + sess->recv_done, buf, res);
1009 sess->recv_done += res;
1010 sess->recv_buf[sess->recv_done] = 0;
1011 }
1012
1013 if (res == 0 && sess->recv_buf == NULL) {
1014 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");
1015 e->event.failure = GG_FAILURE_CONNECTING;
1016 return GG_ACTION_FAIL;
1017 }
1018
1019 if (res != 0)
1020 return GG_ACTION_WAIT;
1021
1022 gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// received http reply:\n%s", sess->recv_buf);
1023
1024 res = sscanf(sess->recv_buf, "HTTP/1.%*d %3d ", &reply);
1025
1026 /* sprawdzamy, czy wszystko w porządku. */
1027 if (res != 1 || reply != 200) {
1028 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
1029 e->event.failure = GG_FAILURE_CONNECTING;
1030 return GG_ACTION_FAIL;
1031 }
1032
1033 /* szukamy początku treści */
1034 body = strstr(sess->recv_buf, "\r\n\r\n");
1035
1036 if (body == NULL) {
1037 body = strstr(sess->recv_buf, "\n\n");
1038
1039 if (body == NULL) {
1040 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
1041 e->event.failure = GG_FAILURE_CONNECTING;
1042 return GG_ACTION_FAIL;
1043 } else {
1044 body += 2;
1045 }
1046 } else {
1047 body += 4;
1048 }
1049
1050 /* 17591 0 91.197.13.71:8074 91.197.13.71 */
1051 res = sscanf(body, "%d %*d %128s", &reply, host);
1052
1053 if (res != 2) {
1054 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid hub reply, connection failed\n");
1055 e->event.failure = GG_FAILURE_CONNECTING;
1056 return GG_ACTION_FAIL;
1057 }
1058
1059 gg_debug_session(sess, GG_DEBUG_MISC, "reply=%d, host=\"%s\"\n", reply, host);
1060
1061 /* jeśli pierwsza liczba w linii nie jest równa zeru,
1062 * oznacza to, że mamy wiadomość systemową. */
1063 if (reply != 0) {
1064 tmp = strchr(body, '\n');
1065
1066 if (tmp != NULL) {
1067 e->type = GG_EVENT_MSG;
1068 e->event.msg.msgclass = reply;
1069 e->event.msg.sender = 0;
1070 e->event.msg.message = (unsigned char*) strdup(tmp + 1);
1071
1072 if (e->event.msg.message == NULL) {
1073 gg_debug_session(sess, GG_DEBUG_MISC,
1074 "// gg_watch_fd() not enough memory "
1075 "for system message\n");
1076 return GG_ACTION_FAIL;
1077 }
1078 }
1079 }
1080
1081 gg_close(sess);
1082
1083 tmp = strchr(host, ':');
1084
1085 if (tmp != NULL) {
1086 *tmp = 0;
1087 port = atoi(tmp + 1);
1088 }
1089
1090 if (strcmp(host, "notoperating") == 0) {
1091 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n");
1092 e->event.failure = GG_FAILURE_UNAVAILABLE;
1093 return GG_ACTION_FAIL;
1094 }
1095
1096 addr.s_addr = inet_addr(host);
1097 if (addr.s_addr == INADDR_NONE)
1098 addr.s_addr = 0;
1099 sess->server_addr = addr.s_addr;
1100
1101 free(sess->recv_buf);
1102 sess->recv_buf = NULL;
1103 sess->recv_done = 0;
1104
1105 if (sess->state != GG_STATE_READING_PROXY_HUB) {
1106 if (sess->port == 0) {
1107 sess->connect_port[0] = port;
1108 sess->connect_port[1] = (port != GG_HTTPS_PORT) ? GG_HTTPS_PORT : 0;
1109 } else {
1110 sess->connect_port[0] = sess->port;
1111 sess->connect_port[1] = 0;
1112 }
1113 } else {
1114 sess->connect_port[0] = (sess->port == 0) ? GG_HTTPS_PORT : sess->port;
1115 sess->connect_port[1] = 0;
1116 }
1117
1118 free(sess->connect_host);
1119 sess->connect_host = strdup(host);
1120
1121 if (sess->connect_host == NULL) {
1122 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory\n");
1123 return GG_ACTION_FAIL;
1124 }
1125
1126 host_white = sess->private_data->host_white_list;
1127 if (!host_white)
1128 host_white = host_white_default;
1129
1130 if (sess->ssl_flag == GG_SSL_REQUIRED && host_white[0] != NULL) {
1131 int host_ok = 0;
1132 char **it;
1133 int host_len;
1134
1135 host_len = strlen(sess->connect_host);
1136
1137 for (it = host_white; *it != NULL; it++) {
1138 const char *white = *it;
1139 int white_len, dom_offset;
1140
1141 white_len = strlen(white);
1142 if (white_len > host_len)
1143 continue;
1144
1145 dom_offset = host_len - white_len;
1146 if (strncasecmp(sess->connect_host + dom_offset, white,
1147 white_len) != 0)
1148 {
1149 continue;
1150 }
1151
1152 if (white_len < host_len) {
1153 if (sess->connect_host[dom_offset - 1] != '.')
1154 continue;
1155 }
1156
1157 host_ok = 1;
1158 break;
1159 }
1160
1161 if (!host_ok) {
1162 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
1163 "// gg_watch_fd() the HUB server returned "
1164 "a host that is not trusted (%s)\n",
1165 sess->connect_host);
1166 e->event.failure = GG_FAILURE_TLS;
1167 return GG_ACTION_FAIL;
1168 }
1169 }
1170
1171 if (sess->state == GG_STATE_READING_HUB)
1172 sess->resolver_host = sess->connect_host;
1173
1174 /* Jeśli łączymy się przez proxy, zacznijmy od początku listy */
1175 sess->resolver_index = 0;
1176
1177 sess->state = (sess->async) ? next_state : alt_state;
1178
1179 return GG_ACTION_NEXT;
1180 }
1181
gg_handle_send_proxy_gg(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)1182 static gg_action_t gg_handle_send_proxy_gg(struct gg_session *sess,
1183 struct gg_event *e, enum gg_state_t next_state,
1184 enum gg_state_t alt_state, enum gg_state_t alt2_state)
1185 {
1186 char *req, *auth;
1187 size_t req_len;
1188 int res;
1189
1190 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() %s\n", gg_debug_state(sess->state));
1191
1192 if (sess->connect_index > 1 || sess->connect_port[sess->connect_index] == 0) {
1193 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of connection candidates\n");
1194 e->event.failure = GG_FAILURE_CONNECTING;
1195 return GG_ACTION_FAIL;
1196 }
1197
1198 auth = gg_proxy_auth();
1199
1200 req = gg_saprintf("CONNECT %s:%d HTTP/1.0\r\n%s\r\n",
1201 sess->connect_host, sess->connect_port[sess->connect_index],
1202 (auth) ? auth : "");
1203
1204 free(auth);
1205
1206 sess->connect_index++;
1207
1208 if (req == NULL) {
1209 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
1210 e->event.failure = GG_FAILURE_PROXY;
1211 return GG_ACTION_FAIL;
1212 }
1213
1214 req_len = strlen(req);
1215
1216 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n%s", req);
1217
1218 res = send(sess->fd, req, req_len, 0);
1219
1220 free(req);
1221
1222 if (res == -1 && errno != EINTR && errno != EAGAIN) {
1223 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
1224 e->event.failure = GG_FAILURE_PROXY;
1225 return GG_ACTION_FAIL;
1226 }
1227
1228 if ((size_t) res < req_len) {
1229 sess->state = alt_state;
1230 sess->check = GG_CHECK_WRITE;
1231 sess->timeout = GG_DEFAULT_TIMEOUT;
1232 } else {
1233 sess->state = next_state;
1234 sess->check = GG_CHECK_READ;
1235 sess->timeout = GG_DEFAULT_TIMEOUT;
1236 }
1237
1238 return GG_ACTION_WAIT;
1239 }
1240
gg_handle_tls_negotiation(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)1241 static gg_action_t gg_handle_tls_negotiation(struct gg_session *sess,
1242 struct gg_event *e, enum gg_state_t next_state,
1243 enum gg_state_t alt_state, enum gg_state_t alt2_state)
1244 {
1245 #if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
1246 int valid_hostname = 0;
1247 #endif
1248
1249 #ifdef GG_CONFIG_HAVE_GNUTLS
1250 unsigned int status;
1251 int res;
1252
1253 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
1254
1255 for (;;) {
1256 res = gnutls_handshake(GG_SESSION_GNUTLS(sess));
1257
1258 if (res == GNUTLS_E_AGAIN) {
1259 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_AGAIN\n");
1260
1261 if (gnutls_record_get_direction(GG_SESSION_GNUTLS(sess)) == 0)
1262 sess->check = GG_CHECK_READ;
1263 else
1264 sess->check = GG_CHECK_WRITE;
1265 sess->timeout = GG_DEFAULT_TIMEOUT;
1266 return GG_ACTION_WAIT;
1267 }
1268
1269 if (res == GNUTLS_E_INTERRUPTED) {
1270 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_INTERRUPTED\n");
1271 continue;
1272 }
1273
1274 if (res != GNUTLS_E_SUCCESS) {
1275 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
1276 " TLS handshake error: %d, %s\n",
1277 res, gnutls_strerror(res));
1278 e->event.failure = GG_FAILURE_TLS;
1279 return GG_ACTION_FAIL;
1280 }
1281
1282 break;
1283 }
1284
1285 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n");
1286 gg_debug_session(sess, GG_DEBUG_MISC, "// cipher: VERS-%s:%s:%s:%s:COMP-%s\n",
1287 gnutls_protocol_get_name(gnutls_protocol_get_version(GG_SESSION_GNUTLS(sess))),
1288 gnutls_cipher_get_name(gnutls_cipher_get(GG_SESSION_GNUTLS(sess))),
1289 gnutls_kx_get_name(gnutls_kx_get(GG_SESSION_GNUTLS(sess))),
1290 gnutls_mac_get_name(gnutls_mac_get(GG_SESSION_GNUTLS(sess))),
1291 gnutls_compression_get_name(gnutls_compression_get(GG_SESSION_GNUTLS(sess))));
1292
1293 if (gnutls_certificate_type_get(GG_SESSION_GNUTLS(sess)) == GNUTLS_CRT_X509) {
1294 unsigned int peer_count;
1295 const gnutls_datum_t *peers;
1296 gnutls_x509_crt_t cert;
1297
1298 if (gnutls_x509_crt_init(&cert) == 0) {
1299 peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count);
1300
1301 if (peers != NULL) {
1302 char buf[256];
1303 size_t size;
1304
1305 if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) == 0) {
1306 size = sizeof(buf);
1307 gnutls_x509_crt_get_dn(cert, buf, &size);
1308 gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf);
1309 size = sizeof(buf);
1310 gnutls_x509_crt_get_issuer_dn(cert, buf, &size);
1311 gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
1312
1313 if (gnutls_x509_crt_check_hostname(cert, sess->connect_host) != 0)
1314 valid_hostname = 1;
1315 }
1316 }
1317
1318 gnutls_x509_crt_deinit(cert);
1319 }
1320 }
1321
1322 res = gnutls_certificate_verify_peers2(GG_SESSION_GNUTLS(sess), &status);
1323
1324 if (res != 0 || status != 0) {
1325 gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to"
1326 " verify peer certificate: 0x%x, %d, %s\n", status, res,
1327 gnutls_strerror(res));
1328
1329 if (sess->ssl_flag == GG_SSL_REQUIRED) {
1330 e->event.failure = GG_FAILURE_TLS;
1331 return GG_ACTION_FAIL;
1332 }
1333 } else {
1334 gg_debug_session(sess, GG_DEBUG_MISC, "// verified peer certificate\n");
1335 }
1336
1337
1338 #elif defined GG_CONFIG_HAVE_OPENSSL
1339
1340 X509 *peer;
1341 int res;
1342
1343 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() %s\n", gg_debug_state(sess->state));
1344
1345 res = SSL_connect(GG_SESSION_OPENSSL(sess));
1346
1347 if (res <= 0) {
1348 int err;
1349
1350 err = SSL_get_error(GG_SESSION_OPENSSL(sess), res);
1351
1352 if (res == 0) {
1353 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
1354 e->event.failure = GG_FAILURE_TLS;
1355 return GG_ACTION_FAIL;
1356 }
1357
1358 if (err == SSL_ERROR_WANT_READ) {
1359 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
1360
1361 sess->check = GG_CHECK_READ;
1362 sess->timeout = GG_DEFAULT_TIMEOUT;
1363 return GG_ACTION_WAIT;
1364 } else if (err == SSL_ERROR_WANT_WRITE) {
1365 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
1366
1367 sess->check = GG_CHECK_WRITE;
1368 sess->timeout = GG_DEFAULT_TIMEOUT;
1369 return GG_ACTION_WAIT;
1370 } else {
1371 char buf[256];
1372
1373 ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
1374
1375 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
1376
1377 e->event.failure = GG_FAILURE_TLS;
1378 return GG_ACTION_FAIL;
1379 }
1380 }
1381
1382 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation"
1383 " succeded:\n// cipher: %s\n",
1384 SSL_get_cipher_name(GG_SESSION_OPENSSL(sess)));
1385
1386 peer = SSL_get_peer_certificate(GG_SESSION_OPENSSL(sess));
1387
1388 if (peer == NULL) {
1389 gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n");
1390
1391 if (sess->ssl_flag == GG_SSL_REQUIRED) {
1392 e->event.failure = GG_FAILURE_TLS;
1393 return GG_ACTION_FAIL;
1394 }
1395 } else {
1396 char buf[256];
1397 long res;
1398
1399 X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
1400 gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf);
1401
1402 X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
1403 gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
1404
1405 res = SSL_get_verify_result(GG_SESSION_OPENSSL(sess));
1406
1407 if (res != X509_V_OK) {
1408 gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! "
1409 "unable to verify peer certificate! "
1410 "res=%ld\n", res);
1411
1412 if (sess->ssl_flag == GG_SSL_REQUIRED) {
1413 e->event.failure = GG_FAILURE_TLS;
1414 return GG_ACTION_FAIL;
1415 }
1416 } else {
1417 gg_debug_session(sess, GG_DEBUG_MISC, "// verified peer certificate\n");
1418 }
1419
1420 if (X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, buf, sizeof(buf)) == -1)
1421 buf[0] = 0;
1422
1423 /* Obsługa certyfikatów z wieloznacznikiem */
1424 if (strchr(buf, '*') == buf && strchr(buf + 1, '*') == NULL) {
1425 char *tmp;
1426
1427 tmp = strchr(sess->connect_host, '.');
1428
1429 if (tmp != NULL)
1430 valid_hostname = (strcasecmp(tmp, buf + 1) == 0);
1431 } else {
1432 valid_hostname = (strcasecmp(sess->connect_host, buf) == 0);
1433 }
1434 }
1435
1436 #else
1437
1438 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() no SSL support\n");
1439 e->event.failure = GG_FAILURE_TLS;
1440 return GG_ACTION_FAIL;
1441
1442 #endif
1443
1444 #if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
1445 if (!valid_hostname) {
1446 gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to verify hostname\n");
1447
1448 if (sess->ssl_flag == GG_SSL_REQUIRED) {
1449 e->event.failure = GG_FAILURE_TLS;
1450 return GG_ACTION_FAIL;
1451 }
1452 }
1453
1454 sess->state = next_state;
1455 sess->check = GG_CHECK_READ;
1456 sess->timeout = GG_DEFAULT_TIMEOUT;
1457
1458 return GG_ACTION_WAIT;
1459 #endif
1460 }
1461
gg_handle_reading_proxy_gg(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)1462 static gg_action_t gg_handle_reading_proxy_gg(struct gg_session *sess,
1463 struct gg_event *e, enum gg_state_t next_state,
1464 enum gg_state_t alt_state, enum gg_state_t alt2_state)
1465 {
1466 char buf[256];
1467 int res;
1468 int reply;
1469 char *body;
1470
1471 res = recv(sess->fd, buf, sizeof(buf), 0);
1472
1473 gg_debug_session(sess, GG_DEBUG_MISC, "recv() = %d\n", res);
1474
1475 if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
1476 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
1477 "non-critical recv error (errno=%d, %s)\n",
1478 errno, strerror(errno));
1479 return GG_ACTION_WAIT;
1480 }
1481
1482 if (res == -1) {
1483 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() recv "
1484 "error (errno=%d, %s)\n", errno, strerror(errno));
1485 e->event.failure = GG_FAILURE_CONNECTING;
1486 return GG_ACTION_FAIL;
1487 }
1488
1489 if (res != 0) {
1490 char *tmp;
1491
1492 tmp = realloc(sess->recv_buf, sess->recv_done + res + 1);
1493
1494 if (tmp == NULL) {
1495 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for http reply\n");
1496 return GG_ACTION_FAIL;
1497 }
1498
1499 sess->recv_buf = tmp;
1500 memcpy(sess->recv_buf + sess->recv_done, buf, res);
1501 sess->recv_done += res;
1502 sess->recv_buf[sess->recv_done] = 0;
1503 }
1504
1505 if (res == 0 && sess->recv_buf == NULL) {
1506 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");
1507 e->event.failure = GG_FAILURE_CONNECTING;
1508 return GG_ACTION_FAIL;
1509 }
1510
1511 /* szukamy początku treści */
1512 body = strstr(sess->recv_buf, "\r\n\r\n");
1513
1514 if (body == NULL) {
1515 body = strstr(sess->recv_buf, "\n\n");
1516
1517 if (body == NULL) {
1518 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
1519 e->event.failure = GG_FAILURE_CONNECTING;
1520 return GG_ACTION_FAIL;
1521 } else {
1522 body += 2;
1523 }
1524 } else {
1525 body += 4;
1526 }
1527
1528 gg_debug_session(sess, GG_DEBUG_MISC, "// found body!\n");
1529
1530 gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// received proxy reply:\n%s\n", sess->recv_buf);
1531
1532 res = sscanf(sess->recv_buf, "HTTP/1.%*d %3d ", &reply);
1533
1534 gg_debug_session(sess, GG_DEBUG_MISC, "res = %d, reply = %d\n", res, reply);
1535
1536 /* sprawdzamy, czy wszystko w porządku. */
1537 if (res != 1 || reply != 200) {
1538 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
1539 e->event.failure = GG_FAILURE_CONNECTING;
1540 return GG_ACTION_FAIL;
1541 }
1542
1543 if (sess->ssl_flag != GG_SSL_DISABLED) {
1544 if (gg_session_init_ssl(sess) == -1) {
1545 e->event.failure = GG_FAILURE_TLS;
1546 return GG_ACTION_FAIL;
1547 }
1548
1549 /* Teoretycznie SSL jest inicjowany przez klienta, więc serwer
1550 * nie powinien niczego wysłać. */
1551 if (sess->recv_buf + sess->recv_done > body) {
1552 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unexpected SSL data\n");
1553 e->event.failure = GG_FAILURE_TLS;
1554 return GG_ACTION_FAIL;
1555 }
1556
1557 free(sess->recv_buf);
1558 sess->recv_buf = NULL;
1559 sess->recv_done = 0;
1560
1561 sess->state = alt_state;
1562 sess->check = GG_CHECK_WRITE;
1563 sess->timeout = GG_DEFAULT_TIMEOUT;
1564
1565 return GG_ACTION_WAIT;
1566 }
1567
1568 sess->state = next_state;
1569 sess->check = GG_CHECK_READ;
1570 sess->timeout = GG_DEFAULT_TIMEOUT; /* Pierwszy pakiet musi przyjść */
1571
1572 /* Jeśli zbuforowaliśmy za dużo, przeanalizuj */
1573
1574 if (sess->recv_buf + sess->recv_done > body) {
1575 sess->recv_done = sess->recv_done - (body - sess->recv_buf);
1576 memmove(sess->recv_buf, body, sess->recv_done);
1577 sess->state = alt2_state;
1578 return GG_ACTION_NEXT;
1579 } else {
1580 free(sess->recv_buf);
1581 sess->recv_buf = NULL;
1582 sess->recv_done = 0;
1583 }
1584
1585 return GG_ACTION_WAIT;
1586 }
1587
gg_handle_connected(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)1588 static gg_action_t gg_handle_connected(struct gg_session *sess,
1589 struct gg_event *e, enum gg_state_t next_state,
1590 enum gg_state_t alt_state, enum gg_state_t alt2_state)
1591 {
1592 #if 0
1593 char buf[1024];
1594 int res;
1595
1596 if (gg_send_queued_data(sess) == -1)
1597 return GG_ACTION_FAIL;
1598
1599 res = gg_read(sess, buf, sizeof(buf));
1600
1601 if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
1602 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
1603 "non-critical read error (errno=%d, %s)\n",
1604 errno, strerror(errno));
1605 return GG_ACTION_WAIT;
1606 }
1607
1608 if (res == -1 || res == 0) {
1609 if (res == -1) {
1610 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
1611 " read error (errno=%d, %s)\n",
1612 errno, strerror(errno));
1613 } else {
1614 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
1615 " connection closed\n");
1616 }
1617
1618 if (sess->state == GG_STATE_DISCONNECTING && res == 0) {
1619 e->type = GG_EVENT_DISCONNECT_ACK;
1620 } else if (sess->state == GG_STATE_READING_KEY) {
1621 e->event.failure = GG_FAILURE_INVALID;
1622 return GG_ACTION_FAIL;
1623 }
1624
1625 return GG_ACTION_FAIL;
1626 }
1627
1628 gg_debug_dump(sess, GG_DEBUG_DUMP, buf, res);
1629
1630 if (gg_session_handle_data(sess, buf, res, e) == -1)
1631 return GG_ACTION_FAIL;
1632
1633 if (sess->send_buf != NULL)
1634 sess->check |= GG_CHECK_WRITE;
1635
1636 return GG_ACTION_WAIT;
1637 #else
1638 struct gg_header *gh;
1639
1640 if (gg_send_queued_data(sess) == -1)
1641 return GG_ACTION_FAIL;
1642
1643 gh = gg_recv_packet(sess);
1644
1645 if (gh == NULL) {
1646 if (sess->state == GG_STATE_DISCONNECTING) {
1647 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection broken expectedly\n");
1648 e->type = GG_EVENT_DISCONNECT_ACK;
1649 return GG_ACTION_WAIT;
1650 }
1651
1652 if (errno != EAGAIN) {
1653 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
1654 " gg_recv_packet failed (errno=%d, %s)\n",
1655 errno, strerror(errno));
1656 return GG_ACTION_FAIL;
1657 }
1658 } else {
1659 if (gg_session_handle_packet(sess, gh->type,
1660 (const char *) gh + sizeof(struct gg_header),
1661 gh->length, e) == -1)
1662 {
1663 free(gh);
1664 return GG_ACTION_FAIL;
1665 }
1666
1667 free(gh);
1668 }
1669
1670 sess->check = GG_CHECK_READ;
1671
1672 if (sess->send_buf != NULL)
1673 sess->check |= GG_CHECK_WRITE;
1674
1675 return GG_ACTION_WAIT;
1676 #endif
1677 }
1678
gg_handle_error(struct gg_session * sess,struct gg_event * e,enum gg_state_t next_state,enum gg_state_t alt_state,enum gg_state_t alt2_state)1679 static gg_action_t gg_handle_error(struct gg_session *sess, struct gg_event *e,
1680 enum gg_state_t next_state, enum gg_state_t alt_state,
1681 enum gg_state_t alt2_state)
1682 {
1683 struct gg_session_private *p = sess->private_data;
1684
1685 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_handle_error() failure=%d\n", p->socket_failure);
1686
1687 e->event.failure = p->socket_failure;
1688
1689 return GG_ACTION_FAIL;
1690 }
1691
1692 static const gg_state_transition_t handlers[] =
1693 {
1694 /* style:maxlinelength:start-ignore */
1695 { GG_STATE_RESOLVE_HUB_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_HUB, GG_STATE_SEND_HUB, 0 },
1696 { GG_STATE_RESOLVE_GG_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_GG, GG_STATE_READING_KEY, 0 },
1697 { GG_STATE_RESOLVE_PROXY_HUB_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_PROXY_HUB, GG_STATE_SEND_PROXY_HUB, 0 },
1698 { GG_STATE_RESOLVE_PROXY_GG_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_PROXY_GG, GG_STATE_SEND_PROXY_GG, 0 },
1699
1700 { GG_STATE_RESOLVE_HUB_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_HUB, GG_STATE_SEND_HUB, 0 },
1701 { GG_STATE_RESOLVE_GG_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_GG, GG_STATE_READING_KEY, 0 },
1702 { GG_STATE_RESOLVE_PROXY_HUB_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_PROXY_HUB, GG_STATE_SEND_PROXY_HUB, 0 },
1703 { GG_STATE_RESOLVE_PROXY_GG_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_PROXY_GG, GG_STATE_SEND_PROXY_GG, 0 },
1704
1705 { GG_STATE_RESOLVING_HUB, gg_handle_resolving, GG_STATE_CONNECT_HUB, 0, 0 },
1706 { GG_STATE_RESOLVING_GG, gg_handle_resolving, GG_STATE_CONNECT_GG, 0, 0 },
1707 { GG_STATE_RESOLVING_PROXY_HUB, gg_handle_resolving, GG_STATE_CONNECT_PROXY_HUB, 0, 0 },
1708 { GG_STATE_RESOLVING_PROXY_GG, gg_handle_resolving, GG_STATE_CONNECT_PROXY_GG, 0, 0 },
1709
1710 { GG_STATE_CONNECT_HUB, gg_handle_connect, GG_STATE_CONNECTING_HUB, 0, 0 },
1711 { GG_STATE_CONNECT_PROXY_HUB, gg_handle_connect, GG_STATE_CONNECTING_PROXY_HUB, 0, 0 },
1712 { GG_STATE_CONNECT_PROXY_GG, gg_handle_connect, GG_STATE_CONNECTING_PROXY_GG, 0, 0 },
1713
1714 { GG_STATE_CONNECT_GG, gg_handle_connect_gg, GG_STATE_CONNECTING_GG, 0, 0 },
1715
1716 { GG_STATE_CONNECTING_HUB, gg_handle_connecting, GG_STATE_SEND_HUB, GG_STATE_CONNECT_HUB, 0 },
1717 { GG_STATE_CONNECTING_PROXY_HUB, gg_handle_connecting, GG_STATE_SEND_PROXY_HUB, GG_STATE_CONNECT_PROXY_HUB, 0 },
1718 { GG_STATE_CONNECTING_PROXY_GG, gg_handle_connecting, GG_STATE_SEND_PROXY_GG, GG_STATE_CONNECT_PROXY_GG, 0 },
1719
1720 { GG_STATE_CONNECTING_GG, gg_handle_connecting_gg, GG_STATE_READING_KEY, GG_STATE_CONNECT_GG, GG_STATE_TLS_NEGOTIATION },
1721
1722 { GG_STATE_SEND_HUB, gg_handle_send_hub, GG_STATE_READING_HUB, GG_STATE_SENDING_HUB, 0 },
1723 { GG_STATE_SEND_PROXY_HUB, gg_handle_send_hub, GG_STATE_READING_PROXY_HUB, GG_STATE_SENDING_PROXY_HUB, 0 },
1724
1725 { GG_STATE_SEND_PROXY_GG, gg_handle_send_proxy_gg, GG_STATE_READING_PROXY_GG, GG_STATE_SENDING_PROXY_GG, 0 },
1726
1727 { GG_STATE_SENDING_HUB, gg_handle_sending_hub_proxy, GG_STATE_READING_HUB, 0, 0 },
1728 { GG_STATE_SENDING_PROXY_HUB, gg_handle_sending_hub_proxy, GG_STATE_READING_PROXY_HUB, 0, 0 },
1729 { GG_STATE_SENDING_PROXY_GG, gg_handle_sending_hub_proxy, GG_STATE_READING_PROXY_GG, 0, 0 },
1730
1731 { GG_STATE_READING_HUB, gg_handle_reading_hub_proxy, GG_STATE_RESOLVE_GG_ASYNC, GG_STATE_RESOLVE_GG_SYNC, 0 },
1732 { GG_STATE_READING_PROXY_HUB, gg_handle_reading_hub_proxy, GG_STATE_CONNECT_PROXY_GG, GG_STATE_CONNECT_PROXY_GG, 0 },
1733
1734 { GG_STATE_READING_PROXY_GG, gg_handle_reading_proxy_gg, GG_STATE_READING_KEY, GG_STATE_TLS_NEGOTIATION, GG_STATE_READING_KEY },
1735
1736 { GG_STATE_TLS_NEGOTIATION, gg_handle_tls_negotiation, GG_STATE_READING_KEY, 0, 0 },
1737
1738 { GG_STATE_READING_KEY, gg_handle_connected, 0, 0, 0 },
1739 { GG_STATE_READING_REPLY, gg_handle_connected, 0, 0, 0 },
1740 { GG_STATE_CONNECTED, gg_handle_connected, 0, 0, 0 },
1741 { GG_STATE_DISCONNECTING, gg_handle_connected, 0, 0, 0 },
1742 { GG_STATE_ERROR, gg_handle_error, 0, 0, 0 },
1743 /* style:maxlinelength:end-ignore */
1744 };
1745
gg_eventqueue_add(struct gg_session * sess)1746 struct gg_event *gg_eventqueue_add(struct gg_session *sess)
1747 {
1748 struct gg_event *ge;
1749 gg_eventqueue_t *queue_el, *it;
1750
1751 queue_el = gg_new0(sizeof(gg_eventqueue_t));
1752 ge = gg_new0(sizeof(struct gg_event));
1753
1754 if (queue_el == NULL || ge == NULL) {
1755 free(queue_el);
1756 free(ge);
1757 return NULL;
1758 }
1759
1760 ge->type = GG_EVENT_NONE;
1761
1762 queue_el->event = ge;
1763 if (sess->private_data->event_queue == NULL)
1764 sess->private_data->event_queue = queue_el;
1765 else {
1766 it = sess->private_data->event_queue;
1767 while (it->next != NULL)
1768 it = it->next;
1769 it->next = queue_el;
1770 }
1771
1772 return ge;
1773 }
1774
1775 /**
1776 * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
1777 *
1778 * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
1779 * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
1780 * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
1781 *
1782 * \param sess Struktura sesji
1783 *
1784 * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
1785 *
1786 * \ingroup events
1787 */
gg_watch_fd(struct gg_session * sess)1788 struct gg_event *gg_watch_fd(struct gg_session *sess)
1789 {
1790 struct gg_event *ge;
1791 struct gg_session_private *priv;
1792
1793 gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
1794
1795 if (sess == NULL) {
1796 errno = EFAULT;
1797 return NULL;
1798 }
1799
1800 priv = sess->private_data;
1801
1802 if (priv->event_queue != NULL) {
1803 gg_eventqueue_t *next;
1804
1805 ge = priv->event_queue->event;
1806 next = priv->event_queue->next;
1807 free(priv->event_queue);
1808 priv->event_queue = next;
1809
1810 if (next == NULL) {
1811 sess->check = priv->check_after_queue;
1812 sess->fd = priv->fd_after_queue;
1813 }
1814 return ge;
1815 }
1816
1817 ge = malloc(sizeof(struct gg_event));
1818
1819 if (ge == NULL) {
1820 gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
1821 return NULL;
1822 }
1823
1824 memset(ge, 0, sizeof(struct gg_event));
1825
1826 ge->type = GG_EVENT_NONE;
1827
1828 for (;;) {
1829 unsigned int i, found = 0;
1830 gg_action_t res;
1831
1832 res = GG_ACTION_FAIL;
1833
1834 for (i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) {
1835 if (handlers[i].state == (enum gg_state_t) sess->state) {
1836 gg_debug_session(sess, GG_DEBUG_MISC,
1837 "// gg_watch_fd() %s\n",
1838 gg_debug_state(sess->state));
1839 res = (*handlers[i].handler)(sess, ge,
1840 handlers[i].next_state,
1841 handlers[i].alt_state,
1842 handlers[i].alt2_state);
1843 found = 1;
1844 break;
1845 }
1846 }
1847
1848 if (!found) {
1849 gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
1850 "// gg_watch_fd() invalid state %s\n",
1851 gg_debug_state(sess->state));
1852 ge->event.failure = GG_FAILURE_INTERNAL;
1853 }
1854
1855 if (!sess->async && ge->type == GG_EVENT_NONE && res == GG_ACTION_WAIT)
1856 res = GG_ACTION_NEXT;
1857
1858 switch (res) {
1859 case GG_ACTION_WAIT:
1860 if (priv->event_queue != NULL) {
1861 priv->fd_after_queue = sess->fd;
1862 priv->check_after_queue = sess->check;
1863 /* wymuszamy ponowne wywołanie gg_watch_fd */
1864 sess->fd = gg_get_dummy_fd(sess);
1865 if (sess->fd < 0)
1866 sess->fd = priv->fd_after_queue;
1867 sess->check = GG_CHECK_READ | GG_CHECK_WRITE;
1868 }
1869 return ge;
1870
1871 case GG_ACTION_NEXT:
1872 continue;
1873
1874 case GG_ACTION_FAIL:
1875 sess->state = GG_STATE_IDLE;
1876
1877 gg_close(sess);
1878
1879 if (ge->event.failure != 0) {
1880 ge->type = GG_EVENT_CONN_FAILED;
1881 } else {
1882 free(ge);
1883 ge = NULL;
1884 }
1885
1886 return ge;
1887
1888 /* Celowo nie ma default */
1889 }
1890 }
1891 }
1892
1893 /*
1894 * Local variables:
1895 * c-indentation-style: k&r
1896 * c-basic-offset: 8
1897 * indent-tabs-mode: notnil
1898 * End:
1899 *
1900 * vim: shiftwidth=8:
1901 */
1902