-fixes
[oweals/gnunet.git] / src / gns / gnocksy / gnocksy.c
1 /*
2  * The GNS Socks5 Proxy
3  */
4
5 #include <stdio.h>
6 /**
7  *
8  * Note: Only supports addr type 3 (domain) for now.
9  * Chrome uses it automatically
10  * For FF: about:config -> network.proxy.socks_remote_dns true
11  *
12  * TODO
13  * - zkey shorten
14  * - LEHO replacement and glue
15  * - SSL
16  */
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <sys/epoll.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <netdb.h>
28 #include <arpa/inet.h>
29 #include <microhttpd.h>
30 #include <curl/curl.h>
31 #include <regex.h>
32
33 #include "protocol.h"
34 #include "gns_glue.h"
35
36 #define MAXEVENTS 64
37
38 #define DEBUG 0
39 #define VERBOSE 1
40
41 #define HTML_HDR_CONTENT "Content-Type: text/html\r\n"
42
43 #define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
44
45 #define RE_N_MATCHES 4
46
47 #define HTTP_PORT 80
48 #define HTTPS_PORT 443
49
50 static struct MHD_Daemon *mhd_daemon;
51 static regex_t re_dotplus;
52
53 static size_t
54 curl_write_data (void *buffer, size_t size, size_t nmemb, void* cls)
55 {
56   const char* page = buffer;
57   uint64_t bytes = size * nmemb;
58   struct socks5_bridge* br = cls;
59   int ret;
60
61   int nomatch;
62   regmatch_t m[RE_N_MATCHES];
63   char* hostptr;
64   char* plusptr;
65   char* p;
66   char new_host[256];
67   char to_exp[256];
68   uint64_t bytes_copied = 0;
69
70   char new_buf[CURL_MAX_WRITE_SIZE+1];
71   p = new_buf;
72
73   pthread_mutex_lock ( &br->m_buf );
74   if (br->MHD_CURL_BUF_STATUS == BUF_WAIT_FOR_MHD)
75   {
76     pthread_mutex_unlock ( &br->m_buf );
77     return CURL_WRITEFUNC_PAUSE;
78   }
79
80   /* do regex magic */
81   if ( br->res_is_html )
82   {
83     printf ("result is html text\n");
84     memset (new_buf, 0, sizeof(new_buf));
85     memcpy (new_buf, page, bytes);
86
87  
88     while (1)
89     {
90       nomatch = regexec ( &re_dotplus, p, RE_N_MATCHES, m, 0);
91
92       if (nomatch)
93       {
94         if (DEBUG)
95           printf ("No more matches\n");
96         if ((p-new_buf) < 0)
97         {
98           if (DEBUG)
99             printf ("Error p<buf!\n");
100           break;
101         }
102         memcpy ( br->MHD_CURL_BUF+bytes_copied, p, bytes-(p-new_buf));
103         bytes_copied += bytes-(p-new_buf);
104         break;
105       }
106
107       if (DEBUG)
108         printf ("Got match\n");
109
110       if (m[1].rm_so != -1)
111       {
112         hostptr = p+m[1].rm_so;
113         if (DEBUG)
114           printf ("Copying %d bytes.\n", (hostptr-p));
115         memcpy (br->MHD_CURL_BUF+bytes_copied, p, (hostptr-p));
116         bytes_copied += (hostptr-p);
117         memset (new_host, 0, sizeof(new_host));
118         memset (to_exp, 0, sizeof (to_exp));
119         memcpy (to_exp, hostptr, (m[1].rm_eo-m[1].rm_so));
120
121         gns_glue_expand_and_shorten ( to_exp,
122                                       br->host,
123                                       new_host );
124         if (DEBUG)
125         {
126           printf ("Glue fin\n");
127           printf ("Copying new name %s \n", new_host);
128         }
129         memcpy ( br->MHD_CURL_BUF+bytes_copied, new_host,
130                  strlen (new_host) );
131         bytes_copied += strlen (new_host);
132         p += m[3].rm_so+1;
133
134         if (DEBUG)
135           printf ("Done. Next in %d bytes\n", m[3].rm_so);
136
137         //TODO check buf lenghts!
138       }
139     }
140     br->MHD_CURL_BUF_SIZE = bytes_copied;
141   }
142   else
143   {
144     memcpy (br->MHD_CURL_BUF, buffer, bytes);
145     br->MHD_CURL_BUF_SIZE = bytes;
146   }
147
148   br->MHD_CURL_BUF_STATUS = BUF_WAIT_FOR_MHD;
149
150   pthread_mutex_unlock ( &br->m_buf );
151
152
153   //MHD_destroy_response (response);
154   if (DEBUG)
155     printf( "buffer:\n%s\n", (char*)br->MHD_CURL_BUF );
156   return bytes;
157 }
158
159 static size_t
160 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void* cls)
161 {
162   size_t bytes = size * nmemb;
163   struct socks5_bridge* br = cls;
164   char hdr[bytes+1];
165
166   memcpy(hdr, buffer, bytes);
167   hdr[bytes] = '\0';
168   
169   if (DEBUG)
170     printf ("got hdr: %s", hdr);
171
172   if (0 == strcmp(hdr, HTML_HDR_CONTENT))
173     br->res_is_html = 1;
174
175   return bytes;
176 }
177
178
179 /* 
180  * Create an ipv4/6 tcp socket for a given port
181  *
182  * @param port the port to bind to
183  * @return the file descriptor of the socket or -1
184  */
185 static int
186 create_socket (char *port)
187 {
188   struct addrinfo hints;
189   struct addrinfo *result, *rp;
190   int s, sfd;
191
192   memset (&hints, 0, sizeof (struct addrinfo));
193   hints.ai_family = AF_UNSPEC;
194   hints.ai_socktype = SOCK_STREAM;
195   hints.ai_flags = AI_PASSIVE;
196
197   s = getaddrinfo (NULL, port, &hints, &result);
198   if (s != 0)
199   {
200     fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s));
201     return -1;
202   }
203
204   for (rp = result; rp != NULL; rp = rp->ai_next)
205   {
206     sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
207     if (sfd == -1)
208       continue;
209
210     s = bind (sfd, rp->ai_addr, rp->ai_addrlen);
211     if (s == 0)
212     {
213       break;
214     }
215     close(sfd);
216   }
217
218   if (rp == NULL)
219   {
220     fprintf (stderr, "Could not bind\n");
221     return -1;
222   }
223
224   freeaddrinfo (result);
225
226   return sfd;
227 }
228
229
230 /*
231  * Make socket with fd non blocking
232  *
233  * @param fd the file descriptor of the socket
234  * @return -1 on error
235  */
236 static int
237 setnonblocking (int fd)
238 {
239   int flags, s;
240
241   flags = fcntl (fd, F_GETFL, 0);
242   if (flags == -1)
243   {
244     perror ("fcntl");
245     return -1;
246   }
247
248   flags |= O_NONBLOCK;
249   s = fcntl (fd, F_SETFL, flags);
250   if (s == -1)
251   {
252     perror ("fcntl");
253     return -1;
254   }
255
256   return 0;
257 }
258
259 /**
260  * Checks if name is in tld
261  *
262  * @param name the name to check
263  * @param tld the TLD to check for
264  * @return -1 if name not in tld
265  */
266 static int
267 is_tld (const char* name, const char* tld)
268 {
269   int offset = 0;
270
271   if (strlen (name) <= strlen (tld))
272     return -1;
273
274   offset = strlen (name) - strlen (tld);
275   if (strcmp (name+offset, tld) != 0)
276     return -1;
277
278   return 0;
279 }
280
281
282 /*
283  * Connect to host specified in phost
284  *
285  * @param phost the hostentry containing the IP
286  * @return fd to the connection or -1 on error
287  */
288 static int
289 connect_to_domain (struct hostent* phost, uint16_t srv_port)
290 {
291   uint32_t srv_ip;
292   struct sockaddr_in srv_addr;
293   struct in_addr *sin_addr;
294   int conn_fd;
295
296
297   sin_addr = (struct in_addr*)(phost->h_addr);
298   srv_ip = sin_addr->s_addr;
299   conn_fd = socket(AF_INET, SOCK_STREAM, 0);
300   memset(&srv_addr, 0, sizeof(srv_addr));
301   srv_addr.sin_family = AF_INET;
302   srv_addr.sin_addr.s_addr = srv_ip;
303   srv_addr.sin_port = srv_port;
304   if (DEBUG)
305     printf("target server: %s:%u\n", inet_ntoa(srv_addr.sin_addr), 
306            ntohs(srv_port));
307
308   if (connect (conn_fd, (struct sockaddr*)&srv_addr,
309                sizeof (struct sockaddr)) < 0)
310   {
311    printf("socket request error...\n");
312    close(conn_fd);
313    return -1;
314   }
315   
316   setnonblocking(conn_fd);
317
318   return conn_fd;
319 }
320
321 static int
322 access_cb (void* cls,
323            const struct sockaddr *addr,
324            socklen_t addrlen)
325 {
326   printf ("access cb called\n");
327   return MHD_YES;
328 }
329
330 static void
331 fetch_url (struct socks5_bridge* br)
332 {
333
334   CURLcode ret;
335
336   br->curl = curl_easy_init();
337
338   /* TODO optionally do LEHO stuff here */
339
340   if (br->curl)
341     {
342       curl_easy_setopt (br->curl, CURLOPT_URL, br->full_url);
343       curl_easy_setopt (br->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
344       curl_easy_setopt (br->curl, CURLOPT_HEADERDATA, br);
345       curl_easy_setopt (br->curl, CURLOPT_WRITEFUNCTION, &curl_write_data);
346       curl_easy_setopt (br->curl, CURLOPT_WRITEDATA, br);
347       ret = curl_easy_perform (br->curl);
348       free (br->full_url);
349       pthread_mutex_lock ( &br->m_done );
350       br->is_done = 1;
351       pthread_mutex_unlock ( &br->m_done );
352
353       curl_easy_cleanup (br->curl);
354       
355       if (ret == CURLE_OK)
356       {
357         printf("all good on the curl end\n");
358         return;
359       }
360       printf("error on the curl end %s\n", curl_easy_strerror(ret));
361     }
362 }
363
364 static ssize_t
365 mhd_content_cb (void* cls,
366                 uint64_t pos,
367                 char* buf,
368                 size_t max)
369 {
370   struct socks5_bridge* br = cls;
371
372   pthread_mutex_lock ( &br->m_done );
373   /* if done and buf empty */
374   if ( (br->is_done == 1)  &&
375        (br->MHD_CURL_BUF_STATUS == BUF_WAIT_FOR_CURL) )
376   {
377     printf("done. sending response...\n");
378     br->is_done = 0;
379     pthread_mutex_unlock ( &br->m_done );
380     return MHD_CONTENT_READER_END_OF_STREAM;
381   }
382   pthread_mutex_unlock ( &br->m_done );
383   
384   pthread_mutex_lock ( &br->m_buf );
385   if ( br->MHD_CURL_BUF_STATUS == BUF_WAIT_FOR_CURL )
386   {
387     pthread_mutex_unlock ( &br->m_buf );
388     return 0;
389   }
390
391
392
393   if ( br->MHD_CURL_BUF_SIZE > max )
394   {
395     printf("buffer in mhd response too small!\n");
396     pthread_mutex_unlock ( &br->m_buf );
397     return MHD_CONTENT_READER_END_WITH_ERROR;
398   }
399   
400   if (0 != br->MHD_CURL_BUF_SIZE)
401   {
402     printf("copying %d bytes to mhd response at offset %d\n",
403            br->MHD_CURL_BUF_SIZE, pos);
404     memcpy ( buf, br->MHD_CURL_BUF, br->MHD_CURL_BUF_SIZE );
405   }
406   br->MHD_CURL_BUF_STATUS = BUF_WAIT_FOR_CURL;
407   curl_easy_pause (br->curl, CURLPAUSE_CONT);
408   pthread_mutex_unlock ( &br->m_buf );
409
410   return br->MHD_CURL_BUF_SIZE;
411 }
412
413 static int
414 accept_cb (void *cls,
415            struct MHD_Connection *con,
416            const char *url,
417            const char *meth,
418            const char *ver,
419            const char *upload_data,
420            size_t *upload_data_size,
421            void **con_cls)
422 {
423   static int dummy;
424   const char* page = "<html><head><title>gnoxy</title>"\
425                       "</head><body>gnoxy demo</body></html>";
426   struct MHD_Response *response;
427   struct socks5_bridge *br = cls;
428   int ret;
429   char* full_url;
430
431   if (0 != strcmp (meth, "GET"))
432     return MHD_NO;
433   if (&dummy != *con_cls)
434   {
435     *con_cls = &dummy;
436     return MHD_YES;
437   }
438
439   if (0 != *upload_data_size)
440     return MHD_NO;
441
442   *con_cls = NULL;
443   
444   if (-1 == asprintf (&br->full_url, "%s%s", br->host, url))
445   {
446     printf ("error building url!\n");
447     return MHD_NO;
448   }
449   printf ("url %s\n", br->full_url);
450
451   pthread_mutex_lock ( &br->m_done );
452   br->is_done = 0;
453   pthread_mutex_unlock ( &br->m_done );
454   
455   br->MHD_CURL_BUF_STATUS = BUF_WAIT_FOR_CURL;
456   br->res_is_html = 0;
457
458   response = MHD_create_response_from_callback (-1, -1, 
459                                                 &mhd_content_cb,
460                                                 br,
461                                                 NULL); //TODO destroy resp here
462   
463   ret = MHD_queue_response (con, MHD_HTTP_OK, response);
464   pthread_create ( &br->thread, NULL, &fetch_url, br );
465
466   return MHD_YES;
467   
468   
469 }
470
471 static int
472 compile_regex (regex_t *re, const char* rt)
473 {
474   int status;
475   char err[1024];
476   
477   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
478   if (status)
479   {
480     regerror (status, re, err, 1024);
481     printf ("Regex error compiling '%s': %s\n", rt, err);
482     return 1;
483   }
484   return 0;
485 }
486
487
488 int main ( int argc, char *argv[] )
489 {
490   int sfd, s;
491   int efd;
492   struct epoll_event event;
493   struct epoll_event *events;
494   int ev_states[MAXEVENTS];
495   int n, i, j;
496   struct socks5_bridge* br;
497   
498   struct sockaddr in_addr;
499   socklen_t in_len;
500   int infd;
501   char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
502
503   /* port offset for ssl daemons */
504   int i_ssl = 1;
505   
506   int done;
507   ssize_t count;
508   char buf[512];
509   struct socks5_server_hello hello;
510   struct socks5_server_response resp;
511   struct socks5_client_request *req;
512   struct socks5_bridge* new_br;
513   char domain[256];
514   uint8_t dom_len;
515   uint16_t req_port;
516   int conn_fd;
517   struct hostent *phost;
518
519   mhd_daemon = NULL;
520   curl_global_init(CURL_GLOBAL_ALL);
521
522   //compile_regex ( &re_htmlhdr, RE_HTML );
523   compile_regex( &re_dotplus, (char*)RE_DOTPLUS );
524   
525   for (j = 0; j < MAXEVENTS; j++)
526     ev_states[j] = SOCKS5_INIT;
527
528   if (argc != 2)
529   {
530     fprintf (stderr, "Usage: %s [port]\n", argv[0]);
531     exit (EXIT_FAILURE);
532   }
533
534   sfd = create_socket(argv[1]);
535   if (s == -1)
536     abort ();
537
538   s = setnonblocking (sfd);
539   if (s == -1)
540     abort ();
541
542   s = listen (sfd, SOMAXCONN);
543   if (s == -1)
544   {
545     perror ("listen");
546     abort ();
547   }
548
549   efd = epoll_create1 (0);
550   if (efd == -1)
551   {
552     perror ("epoll create");
553     abort ();
554   }
555
556   br = malloc(sizeof (struct socks5_bridge));
557   event.data.ptr = br;
558   br->fd = sfd;
559   br->remote_end = NULL;
560
561   event.events = EPOLLIN | EPOLLET;
562   s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);
563   if (s == -1)
564   {
565     perror ("epoll ctl");
566     abort ();
567   }
568
569   events = calloc (MAXEVENTS, sizeof event);
570
571   while (1)
572   {
573     n = epoll_wait (efd, events, MAXEVENTS, -1);
574     for (i = 0; i < n; i++)
575     {
576       br = (struct socks5_bridge*)(events[i].data.ptr);
577
578       if ((events[i].events & EPOLLERR) ||
579           (events[i].events & EPOLLHUP) ||
580           (!(events[i].events & EPOLLIN)))
581       {
582         fprintf (stderr, "epoll error %d\n", events[i].events);
583         fprintf (stderr, "closing fd %d\n", br->fd);
584         close (br->fd);
585         continue;
586       }
587       else if (sfd == br->fd)
588       {
589         /* New connection(s) */
590         while (1)
591         {
592           
593           in_len = sizeof (in_addr);
594           infd = accept (sfd, &in_addr, &in_len);
595           if (infd == -1)
596           {
597             if ((errno == EAGAIN) ||
598                 (errno == EWOULDBLOCK))
599             {
600               break;
601             }
602             else
603             {
604               perror ("accept");
605               break;
606             }
607           }
608
609           s = getnameinfo (&in_addr, in_len,
610                            hbuf, sizeof (hbuf),
611                            sbuf, sizeof (sbuf),
612                            NI_NUMERICHOST | NI_NUMERICSERV);
613           if (s == -1)
614             abort ();
615
616           s = setnonblocking (infd);
617           if (s == -1)
618             abort ();
619
620           event.events = EPOLLIN | EPOLLET;
621           br = malloc (sizeof (struct socks5_bridge));
622           br->fd = infd;
623           br->addr = in_addr;
624           br->addr_len = in_len;
625           br->remote_end = NULL;
626           br->status = SOCKS5_INIT;
627           event.data.ptr = br;
628
629           s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event);
630           if (s == -1)
631           {
632             perror ("epoll ctl");
633             abort ();
634           }
635         }
636         continue;
637       }
638       else
639       {
640         /* Incoming data */
641         done = 0;
642
643         while (1)
644         {
645
646           memset (buf, 0, sizeof (buf));
647
648           count = read (br->fd, buf, sizeof (buf));
649
650           if (count == -1)
651           {
652             if (errno != EAGAIN)
653             {
654               perror ("read");
655               done = 1;
656             }
657             break;
658           }
659           else if (count == 0)
660           {
661             done = 1;
662             break;
663           }
664           
665           if (br->status == SOCKS5_DATA_TRANSFER)
666           {
667             if (DEBUG)
668             {
669               printf ("Trying to fwd %d bytes from %d to %d!\n" ,
670                     count, br->fd, br->remote_end->fd );
671             }
672             if (br->remote_end)
673               s = write (br->remote_end->fd, buf, count);
674             if (DEBUG)
675                printf ("%d bytes written\n", s);
676           }
677           
678           if (br->status == SOCKS5_INIT)
679           {
680             if (DEBUG)
681               printf ("SOCKS5 init for %d\n", br->fd);
682             hello.version = 0x05;
683             hello.auth_method = 0;
684             write (br->fd, &hello, sizeof (hello));
685             br->status = SOCKS5_REQUEST;
686           }
687           if (br->status == SOCKS5_REQUEST)
688           {
689             req = (struct socks5_client_request*)buf;
690             
691             memset(&resp, 0, sizeof(resp));
692             
693             if (req->addr_type == 3)
694             {
695               dom_len = *((uint8_t*)(&(req->addr_type) + 1));
696               memset(domain, 0, sizeof(domain));
697               strncpy(domain, (char*)(&(req->addr_type) + 2), dom_len);
698               req_port = *((uint16_t*)(&(req->addr_type) + 2 + dom_len));
699
700               if (DEBUG)
701                 printf ("Requested connection is %s:%d\n",
702                         domain,
703                         ntohs(req_port));
704
705               phost = (struct hostent*)gethostbyname (domain);
706               if (phost == NULL)
707               {
708                 if (VERBOSE)
709                   printf ("Resolve %s error!\n" , domain );
710                 resp.version = 0x05;
711                 resp.reply = 0x01;
712                 write (br->fd, &resp, sizeof (struct socks5_server_response));
713                 break;
714               }
715
716               if (DEBUG)
717                 printf ("trying to add %d to MHD\n", br->fd);
718               
719               if ( -1 != is_tld (domain, ".gnunet") )
720               {
721                 strcpy (br->host, domain);
722                 if (HTTP_PORT == ntohs(req_port))
723                 {
724                   br->use_ssl = 0;
725                   if (NULL == mhd_daemon)
726                   {
727                     mhd_daemon =
728                       MHD_start_daemon( MHD_USE_THREAD_PER_CONNECTION,
729                                         8080,
730                                         &access_cb, br,
731                                         &accept_cb, br,
732                                         MHD_OPTION_END);
733                   }
734                   
735                   if (MHD_YES != MHD_add_connection (mhd_daemon,
736                                                      br->fd,
737                                                      &br->addr,
738                                                      br->addr_len))
739                   {
740                     if (VERBOSE)
741                       printf ("Error adding %d to mhd\n", br->fd);
742                   }
743                 }
744
745                 if (HTTPS_PORT == ntohs(req_port))
746                 {
747                   /*
748                    * custom daemon for SSL requests
749                    * TODO make more efficient with
750                    * per name SSL daemons?
751                    */
752                   br->use_ssl = 1;
753                   br->ssl_daemon = 
754                     MHD_start_daemon( MHD_USE_THREAD_PER_CONNECTION |
755                                       MHD_USE_SSL,
756                                       8080+i_ssl,
757                                       NULL, NULL,
758                                       &accept_cb, br,
759                                       MHD_OPTION_HTTPS_MEM_KEY, NULL,
760                                       MHD_OPTION_HTTPS_MEM_CERT, NULL,
761                                       MHD_OPTION_END);
762                   
763                   i_ssl++;
764                   
765                   if (MHD_YES != MHD_add_connection (br->ssl_daemon,
766                                                      br->fd,
767                                                      &br->addr,
768                                                      br->addr_len))
769                   {
770                     if (VERBOSE)
771                       printf ("Error adding %d to mhd\n", br->fd);
772                   }
773                 }
774
775                 
776                 event.events = EPOLLIN | EPOLLET;
777                 epoll_ctl (efd, EPOLL_CTL_DEL, br->fd, &event);
778                 resp.version = 0x05;
779                 resp.reply = 0x00;
780                 resp.reserved = 0x00;
781                 resp.addr_type = 0x01;
782                 write (br->fd, &resp, 10);
783               }
784               else
785               {
786
787                 conn_fd = connect_to_domain (phost, req_port);
788               
789                 if (-1 == conn_fd)
790                 {
791                   if (VERBOSE)
792                     printf("cannot create remote connection from %d to %s:%d\n",
793                            br->fd, domain, ntohs(req_port));
794                   resp.version = 0x05;
795                   resp.reply = 0x01;
796                   write (br->fd, &resp, 10);
797                 }
798                 else
799                 {
800                   if (VERBOSE)
801                     printf("new remote connection %d to %d\n", br->fd, conn_fd);
802                   resp.version = 0x05;
803                   resp.reply = 0x00;
804                   resp.reserved = 0x00;
805                   resp.addr_type = 0x01;
806                 
807                   new_br = malloc (sizeof (struct socks5_bridge));
808                   if (br->remote_end != NULL)
809                     printf ("WARNING remote end was not NULL!\n");
810                   br->remote_end = new_br;
811                   br->status = SOCKS5_DATA_TRANSFER;
812                   new_br->fd = conn_fd;
813                   new_br->remote_end = br;
814                   new_br->status = SOCKS5_DATA_TRANSFER;
815
816                   event.data.ptr = new_br;
817                   event.events = EPOLLIN | EPOLLET;
818                   epoll_ctl (efd, EPOLL_CTL_ADD, conn_fd, &event);
819                   write (br->fd, &resp, 10);
820                 }
821               }
822
823             }
824             else
825             {
826               if (DEBUG)
827                 printf("not implemented address type %02X\n", (int)req->addr_type);
828             }
829           }
830           
831
832           if (s == -1)
833           {
834             perror ("write");
835             abort ();
836           }
837         }
838
839         if (done)
840         {
841           //close (br->fd);
842
843           if (br->remote_end)
844           {
845             //close (br->remote_end->fd);
846             //free(br->remote_end);
847           }
848           //free(br);
849         }
850       }
851     }
852   }
853
854   free (events);
855   MHD_stop_daemon (mhd_daemon);
856   regfree ( &re_dotplus );
857   close (sfd);
858
859   return EXIT_SUCCESS;
860 }