e39dfc18a538264362f06179ec8f4160261d5219
[udpgate.git] / src / network.c
1 /* $Id$
2  * UDP Gateway utility network layer
3  * Copyright (C) 2004 Jan Kratochvil <project-udpgate@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include <glib/gmessages.h>
23 #include <glib/gmain.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <glib/glist.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <glib/giochannel.h>
33 #include <sys/ioctl.h>
34 #include <string.h>
35 #include <glib/galloca.h>
36 #include <glib/gprintf.h>
37 #include <glib/grand.h>
38
39 #include "network.h"
40 #include "packet.h"
41
42
43 /* Config: */
44 #define NETWORK_PATHNAME_PID "/var/run/udpgate.pid"
45 #define SOCK_SOURCE_CHECK_EVENTS (G_IO_IN)      /* |G_IO_PRI */
46 #define SOCK_SOURCE_CHECK_REVENTS (SOCK_SOURCE_CHECK_EVENTS)    /* |G_IO_ERR|G_IO_HUP|G_IO_NVAL */
47 #define SERVER_INADDR 0x511F02EA        /* paulina.vellum.cz = 81.31.2.234; host order */
48 #define SERVER_PORT   9201      /* host order */
49 #define PROBE_INADDR  SERVER_INADDR     /* host order */
50 #define PROBE_PORT    8201      /* host order */
51 #define CLIENT_TIMEOUT_SEC (5*60)
52 #define PROBE_TIMEOUT_SEC (5)
53
54
55 void (*network_notify_hostip)(guint32 hostip_guint32,const gchar *hostip_string);
56
57
58 struct client {
59         GPollFD gpollfd;
60         struct sockaddr_in sockaddr_in_from;
61         guint timeout_id;
62         };
63
64 static GSource *sock_gsource;
65
66 static GList *sock_client_list; /* of 'struct client *', incl. 'master' and 'probe' */
67 static struct client *master;   /* no 'timeout' permitted */
68 static struct client *probe;    /* 'timeout' permitted */
69 static guint32 probe_unique;
70
71 static gboolean write_daemon_running(pid_t pid);
72
73 pid_t is_daemon_running(void)
74 {
75 FILE *f;
76 char buf[LINE_MAX],*got;
77 int pid_int;
78
79         if (sock_gsource)
80                 return getpid();
81
82         if (!(f=fopen(NETWORK_PATHNAME_PID,"r")))
83                 goto err;
84         got=fgets(buf,sizeof(buf),f);
85         fclose(f);      /* FIXME: error ignored */
86         if (got!=buf) {
87 err_unlink:
88                 write_daemon_running((pid_t)-1);        /* unlink */
89 err:
90                 return (pid_t)-1;
91                 }
92         pid_int=atoi(buf);
93         if (pid_int<=1)
94                 goto err_unlink;
95         if (kill((pid_t)pid_int,0)) {
96                 if (errno==ESRCH)
97                         goto err_unlink;
98                 goto err;
99                 }
100         return (pid_t)pid_int;
101 }
102
103 static gboolean write_daemon_running(pid_t pid)
104 {
105 FILE *f;
106
107         if (pid==(pid_t)-1) {
108                 if (unlink(NETWORK_PATHNAME_PID)) {
109                         if (errno!=ENOENT)
110                                 g_warning(_("Error removing PID file \"%s\": %m"),NETWORK_PATHNAME_PID);
111                         return FALSE;
112                         }
113                 return TRUE;
114                 }
115         if (!(f=fopen(NETWORK_PATHNAME_PID,"w"))) {
116                 g_warning(_("Error writing PID %d to \"%s\": %m"),(int)pid,NETWORK_PATHNAME_PID);
117                 return FALSE;
118                 }
119         fprintf(f,"%d\n",(int)pid);     /* errors ignored */
120         fclose(f);      /* FIXME: error ignored */
121         return TRUE;
122 }
123
124 static void client_timeout_remove(struct client *client)
125 {
126         g_return_if_fail(client!=NULL);
127         g_return_if_fail(client!=master);
128
129         if (client->timeout_id) {
130 gboolean errgboolean;
131
132                 errgboolean=g_source_remove(client->timeout_id);
133                 g_assert(errgboolean==TRUE);
134                 client->timeout_id=0;
135                 }
136 }
137
138 static gboolean client_touch_timeout(struct client *client);
139
140 static void client_touch(struct client *client)
141 {
142         g_return_if_fail(client!=NULL);
143         g_return_if_fail(client!=master);
144
145         client_timeout_remove(client);
146         client->timeout_id=g_timeout_add(
147                         (client==probe ? PROBE_TIMEOUT_SEC*1000 : CLIENT_TIMEOUT_SEC*1000),     /* interval; msec */
148                         (GSourceFunc)client_touch_timeout,      /* function */
149                         client);        /* data */
150         g_assert(client->timeout_id!=0);
151 }
152
153 static void client_destroy(struct client *client);
154
155 static gboolean client_touch_timeout(struct client *client)
156 {
157         g_return_val_if_fail(client!=NULL,FALSE);       /* FALSE=>should be removed */
158         g_return_val_if_fail(client!=master,FALSE);     /* FALSE=>should be removed */
159
160         if (client==probe) {
161                 network_stop();
162                 if (network_notify_hostip)
163                         (*network_notify_hostip)(0,NULL);
164                 }
165
166         client_destroy(client);
167
168         return FALSE;   /* GSource should be removed */
169 }
170
171 static struct client *client_new(void);
172
173 static void handle_master(struct client *master)
174 {
175 ssize_t gotlen;
176 struct sockaddr_in sockaddr_in_from;
177 char packet[0x10000];
178 struct client *client;
179 struct sockaddr_in sockaddr_in_server;
180 socklen_t sockaddr_in_from_length;
181
182         g_return_if_fail(master!=NULL);
183
184         sockaddr_in_from_length=sizeof(sockaddr_in_from);
185         while (-1!=(gotlen=recvfrom(
186                         master->gpollfd.fd,     /* s */
187                         packet, /* buf */
188                         sizeof(packet), /* len */
189                         0,      /* flags */
190                         (struct sockaddr *)&sockaddr_in_from,   /* from */
191                         &sockaddr_in_from_length))) {   /* fromlen */
192 GList *clientl;
193
194                 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from))  /* FIXME: errors reporting */
195                         continue;
196                 /* FIXME: Performance: Ugly search... */
197                 for (clientl=sock_client_list;clientl;clientl=clientl->next) {
198                         client=clientl->data;
199                         if (client==master)
200                                 continue;
201                         if (1
202                                         && client->sockaddr_in_from.sin_family     ==sockaddr_in_from.sin_family
203                                         && client->sockaddr_in_from.sin_port       ==sockaddr_in_from.sin_port
204                                         && client->sockaddr_in_from.sin_addr.s_addr==sockaddr_in_from.sin_addr.s_addr)
205                                 break;
206                         }
207                 if (!clientl) {
208                         client=client_new();
209                         client->sockaddr_in_from=sockaddr_in_from;
210                         }
211                 client_touch(client);
212                 UDPGATE_MEMZERO(&sockaddr_in_server);
213                 sockaddr_in_server.sin_family=AF_INET;
214                 sockaddr_in_server.sin_port=htons(SERVER_PORT);
215                 sockaddr_in_server.sin_addr.s_addr=htonl(SERVER_INADDR);
216                 /* FIXME: errors checking */
217                 sendto(
218                                 client->gpollfd.fd,     /* s */
219                                 packet, /* msg */
220                                 gotlen, /* len */
221                                 0,      /* flags */
222                                 (struct sockaddr *)&sockaddr_in_server, /* to */
223                                 sizeof(sockaddr_in_server));    /* tolen */
224                 }
225 }
226
227 static void handle_probe(struct client *probe)
228 {
229 ssize_t gotlen;
230 struct sockaddr_in sockaddr_in_from;
231 char packet[0x10000];
232 socklen_t sockaddr_in_from_length;
233
234         g_return_if_fail(probe!=NULL);
235
236         sockaddr_in_from_length=sizeof(sockaddr_in_from);
237         while (-1!=(gotlen=recvfrom(
238                         master->gpollfd.fd,     /* s */
239                         packet, /* buf */
240                         sizeof(packet), /* len */
241                         0,      /* flags */
242                         (struct sockaddr *)&sockaddr_in_from,   /* from */
243                         &sockaddr_in_from_length))) {   /* fromlen */
244 static GHashTable *got_hash;
245 static gpointer got_unique_gpointer;
246 static gpointer hostip_gpointer;
247 static guint32 hostip_guint32;
248
249                 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from))  /* FIXME: errors reporting */
250                         continue;
251                 if (!(got_hash=packet_disassembly(packet,gotlen)))
252                         continue;
253                 if (!(g_hash_table_lookup_extended(
254                                 got_hash,       /* hash_table */
255                                 GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),        /* lookup_key */
256                                 NULL,   /* orig_key */
257                                 &got_unique_gpointer))) {
258 err_packet_disassembly_destroy_got_hash:
259                         packet_disassembly_destroy(got_hash);
260                         continue;
261                         }
262                 if (GPOINTER_TO_UINT(got_unique_gpointer)!=probe_unique)
263                         goto err_packet_disassembly_destroy_got_hash;
264                 if (!(g_hash_table_lookup_extended(
265                                 got_hash,       /* hash_table */
266                                 GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_INADDR),       /* lookup_key */
267                                 NULL,   /* orig_key */
268                                 &hostip_gpointer)))
269                         goto err_packet_disassembly_destroy_got_hash;
270                 hostip_guint32=GPOINTER_TO_UINT(hostip_gpointer);
271                 packet_disassembly_destroy(got_hash);
272
273                 client_destroy(probe);
274                 if (network_notify_hostip)
275                         (*network_notify_hostip)(hostip_guint32,
276                                         udpgate_printf_alloca("%d.%d.%d.%d",
277                                                         (hostip_guint32>>24U)&0xFFU,
278                                                         (hostip_guint32>>16U)&0xFFU,
279                                                         (hostip_guint32>> 8U)&0xFFU,
280                                                         (hostip_guint32>> 0U)&0xFFU));
281                 }
282 }
283
284 static void handle_client(struct client *client)
285 {
286 ssize_t gotlen;
287 struct sockaddr_in sockaddr_in_from;
288 char packet [0x10000];
289 socklen_t sockaddr_in_from_length;
290
291         g_return_if_fail(client!=NULL);
292         g_return_if_fail(master!=NULL);
293
294         while (-1!=(gotlen=recvfrom(
295                         client->gpollfd.fd,     /* s */
296                         packet, /* buf */
297                         sizeof(packet), /* len */
298                         0,      /* flags */
299                         (struct sockaddr *)&sockaddr_in_from,   /* from */
300                         &sockaddr_in_from_length))) {   /* fromlen */
301                 if (sockaddr_in_from_length!=sizeof(sockaddr_in_from))  /* FIXME: errors reporting */
302                         continue;
303                 client_touch(client);
304                 /* FIXME: errors checking */
305                 sendto(
306                                 master->gpollfd.fd,     /* s */
307                                 packet, /* msg */
308                                 gotlen, /* len */
309                                 0,      /* flags */
310                                 (struct sockaddr *)&client->sockaddr_in_from,   /* to */
311                                 sizeof(client->sockaddr_in_from));      /* tolen */
312                 }
313 }
314
315 static gboolean sock_source_callback(gpointer data /* unused */)
316 {
317 GList *clientl;
318
319         for (clientl=sock_client_list;clientl;clientl=clientl->next) {
320 struct client *client=clientl->data;
321
322                 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS) {
323                         /**/ if (client==master)
324                                 handle_master(client);
325                         else if (client==probe)
326                                 handle_probe(client);
327                         else
328                                 handle_client(client);
329                         }
330                 }
331
332         return TRUE; /* the source should be kept active */
333 }
334
335 static gboolean sock_source_prepare(GSource *source,gint *timeout)
336 {
337         *timeout=-1;
338
339         return FALSE;
340 }
341
342 static gboolean sock_source_check(GSource *source)
343 {
344 GList *clientl;
345
346         for (clientl=sock_client_list;clientl;clientl=clientl->next) {
347 struct client *client=clientl->data;
348
349                 if (client->gpollfd.revents&SOCK_SOURCE_CHECK_REVENTS)
350                         return TRUE;
351                 }
352         return FALSE;
353 }
354
355 static gboolean sock_source_dispatch(GSource *source,GSourceFunc callback,gpointer user_data)
356 {
357         g_assert(callback!=NULL);
358         return (*callback)(user_data);
359 }
360
361 static GSourceFuncs sock_source_watch_funcs={
362                 sock_source_prepare,
363                 sock_source_check,
364                 sock_source_dispatch,
365                 NULL, /* finalize */
366                 };
367
368 static void sock_gsource_destroy(void)
369 {
370         while (sock_client_list)
371                 client_destroy(sock_client_list->data);
372
373         g_assert(master==NULL);
374
375         if (sock_gsource) {
376                 g_source_destroy(sock_gsource);
377                 sock_gsource=NULL;
378                 }
379
380         write_daemon_running((pid_t)-1);        /* unlink */
381 }
382
383 static gboolean sock_gsource_new(void)
384 {
385         if (sock_gsource)
386                 return TRUE;
387
388         g_assert(sock_client_list==NULL);
389
390         /* attach sock_source_callback() to watch for any abnormalities
391          * on our open pipe 'parentheart_fds' and terminate the child if parent dies.
392          */
393         if (!(sock_gsource=g_source_new(&sock_source_watch_funcs,sizeof(GSource)))) {
394                 g_warning("g_source_new(): %m");
395                 return FALSE;
396                 }
397         g_source_set_callback(
398                         sock_gsource,  /* source */
399                         sock_source_callback,  /* func */
400                         NULL, /* data */
401                         NULL);  /* notify */
402         if (!g_source_attach(   /* returns 'guint' id */
403                         sock_gsource,  /* source */
404                         NULL)) {        /* context; NULL means 'default context' */
405                 g_warning("g_source_attach(gsource,NULL): %m");
406                 sock_gsource_destroy();
407                 return FALSE;
408                 }
409         return TRUE;
410 }
411
412 static struct client *client_new(void)
413 {
414 struct client *client;
415 static unsigned long oneul=1;
416 int sock;
417
418         if (!sock_gsource_new())
419                 return FALSE;
420
421         if (-1==(sock=socket(AF_INET,SOCK_DGRAM,0))) {
422                 g_warning("socket(AF_INET,SOCK_DGRAM): %m");
423                 return NULL;
424                 }
425         if (ioctl(sock,FIONBIO,&oneul)) {       /* non-blocking mode */
426                 g_warning("ioctl(sock,FIONBIO,&1): %m");
427                 close(sock);    /* errors ignored */
428                 return NULL;
429                 }
430
431         udpgate_new(client);
432         client->gpollfd.fd=sock;
433         client->gpollfd.events=SOCK_SOURCE_CHECK_EVENTS;
434         client->gpollfd.revents=0;
435         client->timeout_id=0;
436         sock_client_list=g_list_prepend(sock_client_list,client);
437         g_source_add_poll(sock_gsource,&client->gpollfd);
438
439         return client;
440 }
441
442 static void client_destroy(struct client *client)
443 {
444         g_return_if_fail(client!=NULL);
445
446         if (!sock_gsource_new())
447                 return;
448
449         if (client==master) {
450                 master=NULL;
451                 g_assert(client->timeout_id==0);
452                 }
453         else {
454                 if (client==probe)
455                         probe=NULL;
456                 client_timeout_remove(client);
457                 }
458
459         g_source_remove_poll(sock_gsource,&client->gpollfd);
460         sock_client_list=g_list_remove(sock_client_list,client);
461         close(client->gpollfd.fd);      /* errors ignored */
462         g_free(client);
463 }
464
465 static gboolean probe_send(struct client *probe,gint port_local)
466 {
467 struct sockaddr_in sockaddr_in_server;
468 GHashTable *probe_hash;
469 gpointer packet;
470 size_t packet_length;
471
472         g_return_val_if_fail(probe!=NULL,FALSE);
473
474         probe_unique=g_random_int();
475
476         probe_hash=g_hash_table_new(
477                         g_int_hash,     /* hash_func */
478                         g_int_equal);   /* key_equal_func */
479         g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_CLIENT_PORT) ,GUINT_TO_POINTER(port_local));
480         g_hash_table_insert(probe_hash,GUINT_TO_POINTER(PACKET_ELEM_TYPE_DATA_GUINT32),GUINT_TO_POINTER(probe_unique));
481         packet=packet_assembly(&packet_length,probe_hash);
482         g_hash_table_destroy(probe_hash);
483         if (!packet)
484                 return FALSE;
485
486         UDPGATE_MEMZERO(&sockaddr_in_server);
487         sockaddr_in_server.sin_family=AF_INET;
488         sockaddr_in_server.sin_port=htons(PROBE_PORT);
489         sockaddr_in_server.sin_addr.s_addr=htonl(PROBE_INADDR);
490         /* FIXME: errors checking */
491         sendto(
492                         probe->gpollfd.fd,      /* s */
493                         packet, /* msg */
494                         packet_length,  /* len */
495                         0,      /* flags */
496                         (struct sockaddr *)&sockaddr_in_server, /* to */
497                         sizeof(sockaddr_in_server));    /* tolen */
498
499         return TRUE;
500 }
501
502 gboolean network_start(gint port)
503 {
504 pid_t daemon_pid;
505 struct sockaddr_in sockaddr_in;
506
507         g_return_val_if_fail(port>=0,FALSE);
508
509         if ((pid_t)-1!=(daemon_pid=is_daemon_running())) {
510                 g_warning(_("Cannot start network daemon: Daemon is already running on PID %d"),(int)daemon_pid);
511                 return FALSE;
512                 }
513
514         /* Setup 'master': */
515         g_assert(master==NULL);
516         if (!(master=client_new()))
517                 return FALSE;
518         UDPGATE_MEMZERO(&sockaddr_in);
519         sockaddr_in.sin_family=AF_INET;
520         sockaddr_in.sin_port=htons(port);
521         sockaddr_in.sin_addr.s_addr=INADDR_ANY;
522         if (bind(master->gpollfd.fd,(struct sockaddr *)&sockaddr_in,sizeof(sockaddr_in))) {
523                 g_warning("bind(sock,{AF_INET,INADDR_ANY:%d}): %m",(int)port);
524 err_sock_gsource_destroy:
525                 sock_gsource_destroy();
526                 return FALSE;
527                 }
528
529         /* Setup 'probe': */
530         if (!(probe=client_new()))
531                 goto err_sock_gsource_destroy;
532         probe_send(probe,port);
533         client_touch(probe);    /* timeout */
534
535         write_daemon_running(getpid());
536         return TRUE;
537 }
538
539 gboolean network_stop(void)
540 {
541 pid_t daemon_pid;
542 int errno_save;
543
544         if ((pid_t)-1==(daemon_pid=is_daemon_running())) {
545                 g_warning(_("Cannot stop network daemon: Daemon is not running"));
546                 return FALSE;
547                 }
548         if (daemon_pid==getpid()) {
549                 sock_gsource_destroy();
550                 return TRUE;
551                 }
552         errno=0;
553         kill(daemon_pid,SIGKILL);
554         errno_save=errno;
555         if (errno_save)  {
556                 g_warning(udpgate_printf_alloca(_("Unable to stop the daemon at PID %d: %s"),
557                                 (int)daemon_pid,strerror(errno_save)));
558                 return FALSE;
559                 }
560         return TRUE;
561 }