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