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