1 /*  camserv - An internet streaming picture application
2  *
3  *  Copyright (C) 1999-2002  Jon Travis (jtravis@p00p.org)
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; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19 
20 #include "camserv_config.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <signal.h>
25 
26 #include "databuf.h"
27 #include "socket.h"
28 #include "sockset.h"
29 #include "manager.h"
30 #include "sock_field.h"
31 #include "log.h"
32 
33 #define MODNAME "relay"
34 
35 #define BACKLOG  20
36 
37 #ifdef __DragonFly__
38 #include <errno.h>
39 #else
40 extern int errno;
41 #endif
42 
43 #define CLIENT_T_UNK              0
44 #define CLIENT_T_CAMSERV          1
45 #define CLIENT_T_BROWSER          2
46 #define CLIENT_T_PROXY            3
47 #define CLIENT_T_SINGLE           4
48 
49 #define CAMSTATE_SEND_PROXY       1  /* Sending proxy notifier            */
50 #define CAMSTATE_RECV_SIZE        2  /* Receiving the size of the picture */
51 #define CAMSTATE_RECV_PICTURE     3  /* Receiving the actual picture      */
52 
53 #define BROWSERSTATE_SEND_PREAMBLE  0 /* Sending the preamble  */
54 #define BROWSERSTATE_SEND_PICTURE   1 /* Sending the picture   */
55 #define BROWSERSTATE_SEND_SEPERATOR 2 /* Sending the seperator */
56 
57 #define PROXYCLSTATE_SEND_PICSIZE   0 /* Sending the pic size */
58 #define PROXYCLSTATE_SEND_PICTURE   1 /* Sending the actual picture */
59 
60 typedef struct client_data_st {
61   int clienttype;               /* one of CLIENT_T_* */
62   DataBuf *writebuf;
63   DataBuf *readbuf;
64 
65   struct camdata_st {           /* Valid only if clientttype == CAMSERV  */
66     unsigned long int picsize;  /* Picture size in network order */
67     char *picbuf;               /* Actual picture data           */
68     int camstate;               /* State of camera               */
69   } camdata;
70 
71   struct browser_st {
72     int browserstate;
73     void *management_data;
74     int last_pic_id;
75   } browserdata;
76 
77   struct proxycl_st {
78     int proxystate;
79     unsigned long int picsize;  /* Picture size in network order */
80     void *management_data;
81     int last_pic_id;
82     char *pic_data;
83   } proxycldata;
84 
85   char junkbuf[ 1024 ];
86 } ClientData;
87 
88 typedef struct relay_data_st {
89   char camera_ip[ 1024 ];   /* IP of the remote camserv or relay   */
90   int camera_port;          /* Port of the remote camserv or relay */
91   Socket *camera_sock;      /* Connection of camera sock           */
92 } RelayData;
93 
94 #define RANDOMSTRING "ThisRandomString"
95 #define CONTENTTYPE  "image/jpeg"
96 
97 static
get_preamble_text(size_t * len,int multi)98 char *get_preamble_text( size_t *len, int multi ){
99 #define MPREAMBLE_STR "HTTP/1.0 200 OK\n"  \
100   "Content-type: multipart/x-mixed-replace;boundary=" RANDOMSTRING "\n" \
101   "Cache-Control: no-cache\n" \
102   "Cache-Control: private\n" \
103   "Pragma: no-cache\n\n" \
104   "--" RANDOMSTRING "\n" \
105   "Content-type: " CONTENTTYPE "\n\n"
106 #define SPREAMBLE_STR "HTTP/1.0 200 OK\n"  \
107   "Content-type: " CONTENTTYPE "\n" \
108   "Cache-Control: no-cache\n" \
109   "Cache-Control: private\n" \
110   "Pragma: no-cache\n\n"
111 
112   if( multi ) {
113     if( len != NULL ) *len = sizeof( MPREAMBLE_STR ) - 1;
114     return MPREAMBLE_STR;
115   } else {
116     if( len != NULL ) *len = sizeof( SPREAMBLE_STR ) - 1;
117     return SPREAMBLE_STR;
118   }
119 }
120 
121 static
get_seperator_text(size_t * len)122 char *get_seperator_text( size_t *len ){
123 #define SEPERATOR_TEXT "\n--" RANDOMSTRING "\n"  \
124   "Content-type: " CONTENTTYPE "\n\n" /* XXX */
125 
126   if( len != NULL ) *len = sizeof( SEPERATOR_TEXT ) - 1;
127   return SEPERATOR_TEXT;
128 }
129 
130 
131 /*
132  * client_data_new:  Create and initialize a new clientinfo structure.
133  *
134  * Return values:    Returns NULL on failure, else a valid new clientdata
135  *                   on success.
136  */
137 
138 static
client_data_new()139 ClientData *client_data_new(){
140   ClientData *res;
141 
142   if( (res = malloc( sizeof( *res ))) == NULL )
143     return NULL;
144 
145   if( (res->readbuf = databuf_new()) == NULL ){
146     free( res );
147     return NULL;
148   }
149 
150   if( (res->writebuf = databuf_new()) == NULL ){
151     databuf_dest( res->readbuf );
152     free( res );
153     return NULL;
154   }
155 
156   res->clienttype = CLIENT_T_UNK;
157   return res;
158 }
159 
160 /*
161  * client_data_dest:  Destroy a client data structure.
162  *
163  * Arguments:         cldata = Clientdata to destroy
164  */
165 
166 static
client_data_dest(ClientData * cldata)167 void client_data_dest( ClientData *cldata ){
168   databuf_dest( cldata->readbuf );
169   databuf_dest( cldata->writebuf );
170   free( cldata );
171 }
172 
173 /*
174  * relay_connect_camserv:  Connect to a camera server.
175  *
176  * Arguments:              rdata = Relay data with info of where to connect.
177  *
178  * Return values:          Returns -1 if the connection could not be made, else
179  *                         0.  If successful, rdata->camera_sock will be
180  *                         set to the new sock, else it will be set to NULL
181  */
182 
183 static
relay_connect_camserv(SockField_Data * sfdata,RelayData * rdata)184 int relay_connect_camserv( SockField_Data *sfdata, RelayData *rdata ){
185   ClientData *cldata;
186 
187   rdata->camera_sock = socket_connect( rdata->camera_ip, rdata->camera_port );
188   if( rdata->camera_sock == NULL )
189     return -1;
190 
191   if( (cldata = client_data_new()) == NULL ){
192     camserv_log( MODNAME, "Error mallocing clientdata");
193     socket_dest( rdata->camera_sock );
194     return -1;
195   }
196 
197   cldata->clienttype = CLIENT_T_CAMSERV;
198 
199   if( sock_field_manage_socket( sfdata, rdata->camera_sock,
200 				cldata ) == -1 )
201   {
202       camserv_log( MODNAME, "Error managing connect camera socket!");
203       client_data_dest( cldata );
204       socket_dest( rdata->camera_sock );
205       rdata->camera_sock = NULL;
206       return -1;
207   }
208 
209   /* Send the proxy keyword */
210   databuf_buf_set( cldata->writebuf, "PROXY", sizeof( "PROXY" ));
211   cldata->camdata.picsize  = 0;
212   cldata->camdata.picbuf   = NULL;
213   cldata->camdata.camstate = CAMSTATE_SEND_PROXY;
214 
215   return 0;
216 }
217 
218 /*
219  * relay_init:  Initialize the relay.  This involves making the connection
220  *              to the camera server if possible.
221  *
222  * Arguments:   sfdata = Sockfield data passed from the almighty.
223  *
224  * Return values:  Returns SOCKFIELD_OK on success, else SOCKFIELD_CLOSE.
225  */
226 
227 static
relay_init(SockField_Data * sfdata,void * sys_cldata)228 int relay_init( SockField_Data *sfdata, void *sys_cldata ){
229   RelayData *rdata = sys_cldata;
230 
231   if( relay_connect_camserv( sfdata, rdata ) == -1 )
232     camserv_log( MODNAME, "Couldn't connect to camserv: %s %d",
233 	     rdata->camera_ip, rdata->camera_port );
234 
235   return SOCKFIELD_OK;
236 }
237 
238 /*
239  * relay_preclose:  Called by the field loop prior to closing a socket.
240  *                  This routine should cleanup any of the clientdata passed
241  *                  to the fieldloop management routines on socket init.
242  *
243  * Arguments:       sock   = Socket about to be closed.
244  *                  cldata = Clientdata passed in for socket @ manage time.
245  *                  sys_cldata = System clientdata, passed in as argument
246  *                               to main field_loop
247  */
248 
249 static
relay_preclose(Socket * sock,void * cldata,void * sys_cldata)250 void relay_preclose( Socket *sock, void *cldata, void *sys_cldata ){
251   RelayData *rdata = sys_cldata;
252   ClientData *clientdata = cldata;
253 
254   if( clientdata->clienttype == CLIENT_T_CAMSERV ) {
255     camserv_log( MODNAME, "Closing connection to camsera server!" );
256     /* Camera sock is now invalid */
257     rdata->camera_sock = NULL;
258   } else {
259     camserv_log( MODNAME, "Closing client from: \"%s\"",
260 		 socket_query_remote_name( sock ));
261   }
262   client_data_dest( cldata );
263 }
264 
265 /*
266  * read_camera:  This routine is called everytime the camserv socket has
267  *               sent us some data to read.  It is in 2 states the entire
268  *               time -- that of getting the size of the next picture, then
269  *               actually receiving the picture.  After a successful
270  *               receipt of a picture, it is then put into the management bins.
271  *
272  * Arguments:    sfdata = SockField data
273  *               sock   = Socket of the camserv object
274  *               cldata = Clientdata of the camserv object.
275  *
276  * Return values:  Returns one of SOCKFIELD_*
277  */
278 
279 static
read_camera(SockField_Data * sfdata,Socket * sock,ClientData * cldata)280 int read_camera( SockField_Data *sfdata, Socket *sock, ClientData *cldata ){
281   if( cldata->camdata.camstate == CAMSTATE_RECV_SIZE ) {
282     /* We just got the size ... malloc the data and attempt to receive it too*/
283     cldata->camdata.camstate = CAMSTATE_RECV_PICTURE;
284     cldata->camdata.picbuf  = malloc( ntohl( cldata->camdata.picsize ));
285     if( cldata->camdata.picbuf == NULL ) {
286       camserv_log( MODNAME, "Couldn't malloc: %ld bytes for picture!",
287 		   (long)ntohl( cldata->camdata.picsize ));
288       return SOCKFIELD_CLOSE;
289     }
290     databuf_buf_set( cldata->readbuf, cldata->camdata.picbuf,
291 		     ntohl( cldata->camdata.picsize ) );
292   } else {
293     /* Else we just completed getting a whole picture */
294     cldata->camdata.camstate = CAMSTATE_RECV_SIZE;
295     databuf_buf_set( cldata->readbuf, &cldata->camdata.picsize,
296 		     sizeof( cldata->camdata.picsize ) );
297     if( manager_new_picture( cldata->camdata.picbuf,
298 			     ntohl( cldata->camdata.picsize ), 100 ) == -1 )
299     {
300       camserv_log( MODNAME, "Unable to manage picture!");
301       free( cldata->camdata.picbuf );
302       return SOCKFIELD_CLOSE;
303     }
304     sock_field_unhold_write( sfdata );
305   }
306   return SOCKFIELD_OK;
307 }
308 
309 /*
310  * read_client:  Called when a client has sent data to the relay.  We just
311  *               ignore everything the client sends us after initialization.
312  */
313 
314 static
read_client(SockField_Data * sfdata,Socket * sock,ClientData * cldata)315 int read_client( SockField_Data *sfdata, Socket *sock, ClientData *cldata ){
316   /* Just refill the buffer .. client's that write anything, we are really
317      just disregarded */
318   databuf_buf_set( cldata->readbuf, &cldata->junkbuf[ 0 ],
319 		   sizeof( cldata->junkbuf ));
320   return SOCKFIELD_OK;
321 }
322 
323 /*
324  * set_client_writebuf:  Set the writebuffer of the client for the first
325  *                       time.  This is usually the 'init' state for the
326  *                       client.
327  *
328  * Arguments:            cldata = client to set the writebuffer for.
329  */
330 
331 static
set_client_writebuf(ClientData * cldata)332 void set_client_writebuf( ClientData *cldata  ){
333   size_t len;
334   char *cp;
335 
336   if( cldata->clienttype == CLIENT_T_BROWSER ){
337     cp = get_preamble_text( &len, 1 );
338     cldata->browserdata.browserstate = BROWSERSTATE_SEND_PREAMBLE;
339   } else if( cldata->clienttype == CLIENT_T_SINGLE ) {
340     cp = get_preamble_text( &len, 0 );
341     cldata->browserdata.browserstate = BROWSERSTATE_SEND_PREAMBLE;
342   } else if( cldata->clienttype == CLIENT_T_PROXY ) {
343     cp = "";
344     len = 0;
345     cldata->proxycldata.proxystate = PROXYCLSTATE_SEND_PICTURE;
346     cldata->proxycldata.management_data = NULL;
347   } else {
348     camserv_log( MODNAME, "Unknown client state: \"%d\"", cldata->clienttype );
349     cp = "";
350     len = 0;
351   }
352 
353   databuf_buf_set( cldata->writebuf, cp, len );
354 }
355 
356 /*
357  * relay_client_read:  Called by the sockfield loop when a socket has become
358  *                     readable -- On a client of which we have not identified
359  *                     prior, we determine their type by their initial msg.
360  *                     Otherwise on known sockets we attempt to read all the
361  *                     data we are currently trying to get from them, then
362  *                     dispatch them to the correct handling procedures
363  *                     (camserv, vs. client).
364  *
365  * Arguments:          Standard sockfield callback arguments
366  *
367  * Return Values:      Returns one of SOCKFIELD_*
368  */
369 
370 static
relay_client_read(SockField_Data * sfdata,Socket * sock,void * cldata)371 int relay_client_read( SockField_Data *sfdata, Socket *sock, void *cldata ){
372   ClientData *clientdata = cldata;
373   int readres;
374   char sparebuf[ 1024 ];
375 
376   if( clientdata->clienttype == CLIENT_T_UNK ){
377     read( socket_query_fd( sock ), sparebuf, sizeof( sparebuf ) );
378     sparebuf[ sizeof( sparebuf ) - 1 ] = '\0';
379     /* Client initializing itself */
380     if( !strncmp( sparebuf, "GET", 3 ) ){
381       if( strstr( sparebuf, "/singleframe" ))
382 	clientdata->clienttype = CLIENT_T_SINGLE;
383       else
384 	clientdata->clienttype = CLIENT_T_BROWSER;
385     } else if( !strncmp( sparebuf, "PROXY", 5 )) {
386       clientdata->clienttype = CLIENT_T_PROXY;
387     } else {
388       clientdata->clienttype = CLIENT_T_BROWSER;
389     }
390     databuf_buf_set( clientdata->readbuf, &clientdata->junkbuf[ 0 ],
391 		     sizeof( clientdata->junkbuf ));
392 
393     set_client_writebuf( clientdata );
394     sock_field_unhold_write( sfdata );
395     return SOCKFIELD_OK;
396   }
397 
398   readres = databuf_read( clientdata->readbuf, socket_query_fd( sock ));
399   if( readres == -1 ) return SOCKFIELD_CLOSE;
400   if( readres ==  1 ) return SOCKFIELD_OK;
401 
402   if( clientdata->clienttype == CLIENT_T_CAMSERV )
403     return read_camera( sfdata, sock, clientdata );
404   else
405     return read_client( sfdata, sock, clientdata );
406 
407   return SOCKFIELD_OK;
408 }
409 
410 /*
411  * write_camera:  Called when our camserv socket is set to writeable.  For
412  *                most of the time we will just put ourselves on hold, however
413  *                the initial connection with the camserv requires us to
414  *                send the PROXY message.
415  *
416  * Return values:  Returns one of SOCKFIELD_*
417  */
418 
419 static
write_camera(SockField_Data * sfdata,Socket * sock,ClientData * cldata)420 int write_camera( SockField_Data *sfdata, Socket *sock, ClientData *cldata ){
421   if( cldata->camdata.camstate != CAMSTATE_SEND_PROXY ){
422     sock_field_hold_write( sfdata, sock );
423     return SOCKFIELD_OK;
424   }
425 
426   /* Here we have just initialized our proxy with the connected camera.  So
427      we put ourselves in a 'read' mode for the rest of the period */
428   cldata->camdata.camstate = CAMSTATE_RECV_SIZE;
429   databuf_buf_set( cldata->readbuf, &cldata->camdata.picsize,
430 		   sizeof( cldata->camdata.picsize ) );
431   return SOCKFIELD_OK;
432 }
433 
434 /*
435  * write_client_proxy:  Called when a client (who has previously identified
436  *                      itself as a proxy) has become writeable.  Depending
437  *                      on the current state of the proxy, a picture will
438  *                      either be snagged from the management bins, or
439  *                      the picture will be sent.
440  *
441  * Return values:       Returns one of SOCKFIELD_*
442  */
443 
444 static
write_client_proxy(SockField_Data * sfdata,Socket * sock,ClientData * cldata)445 int write_client_proxy( SockField_Data *sfdata, Socket *sock,
446 			ClientData *cldata )
447 {
448   if( cldata->proxycldata.proxystate == PROXYCLSTATE_SEND_PICTURE )
449   {
450     char *pic_data;
451     size_t pic_size;
452     int pic_id;
453 
454     /* Just finished sending a picture */
455     if( cldata->proxycldata.management_data &&
456 	manager_dest_client( cldata->proxycldata.management_data ) == -1 )
457       camserv_log( MODNAME, "Error destroying client proxy management!");
458 
459     /* Get a new picture to send */
460     cldata->proxycldata.management_data =
461       manager_new_client( &pic_data, &pic_size, &pic_id );
462 
463     if( cldata->proxycldata.management_data == NULL ){
464       camserv_log( MODNAME, "Error managing client proxy!  "
465 		   "(no pictures may be available yet)" );
466       sock_field_hold_write( sfdata, sock );
467       return SOCKFIELD_OK;
468     }
469 
470     if( pic_id == cldata->proxycldata.last_pic_id ) { /* Proxy read too fast */
471       manager_dest_client( cldata->proxycldata.management_data );
472       cldata->proxycldata.management_data = NULL;
473       sock_field_hold_write( sfdata, sock );
474       return SOCKFIELD_OK;
475     }
476 
477     cldata->proxycldata.proxystate = PROXYCLSTATE_SEND_PICSIZE;
478     cldata->proxycldata.picsize = htonl( pic_size );
479     cldata->proxycldata.pic_data = pic_data;
480     cldata->proxycldata.last_pic_id = pic_id;
481     databuf_buf_set( cldata->writebuf,
482 		     &cldata->proxycldata.picsize,
483 		     sizeof( cldata->proxycldata.picsize ));
484   } else {
485     /* Else just finished sending the picture size */
486     cldata->proxycldata.proxystate = PROXYCLSTATE_SEND_PICTURE;
487     databuf_buf_set( cldata->writebuf,
488 		     cldata->proxycldata.pic_data,
489 		     ntohl( cldata->proxycldata.picsize ));
490   }
491 
492   return SOCKFIELD_OK;
493 }
494 
495 /*
496  * write_client_regular:  Called when a regular client has been set writeable.
497  *                        in such a case we switch between all of the different
498  *                        states of a client and send the appropriate thing.
499  *
500  * Return values:         Returns one of SOCKFIELD_*
501  */
502 
503 static
write_client_regular(SockField_Data * sfdata,Socket * sock,ClientData * cldata)504 int write_client_regular( SockField_Data *sfdata, Socket *sock,
505 			  ClientData *cldata )
506 {
507   if( cldata->browserdata.browserstate == BROWSERSTATE_SEND_PREAMBLE ||
508       cldata->browserdata.browserstate == BROWSERSTATE_SEND_SEPERATOR )
509   {
510     char *pic_data;
511     size_t pic_size;
512     int pic_id;
513 
514     cldata->browserdata.management_data =
515       manager_new_client( &pic_data, &pic_size, &pic_id );
516     if( cldata->browserdata.management_data == NULL ){
517       camserv_log( MODNAME, "Error managing client!  "
518 		   "(no pictures may be available yet)");
519       sock_field_hold_write( sfdata, sock );
520       return SOCKFIELD_OK;
521     }
522 
523     if( pic_id == cldata->browserdata.last_pic_id ) { /* Client reads fast */
524       manager_dest_client( cldata->browserdata.management_data );
525       cldata->browserdata.management_data = NULL;
526       sock_field_hold_write( sfdata, sock );
527       return SOCKFIELD_OK;
528     }
529 
530     databuf_buf_set( cldata->writebuf, pic_data, pic_size );
531     cldata->browserdata.last_pic_id = pic_id;
532     cldata->browserdata.browserstate = BROWSERSTATE_SEND_PICTURE;
533   } else {
534     char *sep_data;
535     size_t sep_size;
536 
537     if( manager_dest_client( cldata->browserdata.management_data ) == -1 ){
538       camserv_log( MODNAME, "Error destroying client management!");
539     }
540     cldata->browserdata.management_data = NULL;
541     cldata->browserdata.browserstate = BROWSERSTATE_SEND_SEPERATOR;
542     sep_data = get_seperator_text( &sep_size );
543     databuf_buf_set( cldata->writebuf, sep_data, sep_size );
544   }
545   return SOCKFIELD_OK;
546 }
547 
548 static
write_client(SockField_Data * sfdata,Socket * sock,ClientData * cldata)549 int write_client( SockField_Data *sfdata, Socket *sock, ClientData *cldata ){
550   /* If client hasn't initialized itself with us yet, put him on hold */
551   if( cldata->clienttype == CLIENT_T_UNK ){
552     sock_field_hold_write( sfdata, sock );
553     return SOCKFIELD_OK;
554   }
555 
556   if( cldata->clienttype == CLIENT_T_PROXY )
557     return write_client_proxy( sfdata, sock, cldata );
558   else
559     return write_client_regular( sfdata, sock, cldata );
560 
561   return SOCKFIELD_OK;
562 }
563 
564 static
relay_client_write(SockField_Data * sfdata,Socket * sock,void * cldata)565 int relay_client_write( SockField_Data *sfdata, Socket *sock, void *cldata ){
566   ClientData *clientdata = cldata;
567   int writeres;
568 
569   writeres = databuf_write( clientdata->writebuf, socket_query_fd( sock ));
570   if( writeres == -1 ) return SOCKFIELD_CLOSE;
571   if( writeres ==  1 ) return SOCKFIELD_OK;  /* Still data left to write */
572   /* Else we have written all the data we were supposed to */
573 
574   if( clientdata->clienttype == CLIENT_T_CAMSERV )
575     return write_camera( sfdata, sock, clientdata );
576   else
577     return write_client( sfdata, sock, clientdata );
578 
579   return SOCKFIELD_OK;
580 }
581 
582 static
relay_accept(SockField_Data * sfdata,Socket * listen_sock,void * sys_cldata)583 void relay_accept( SockField_Data *sfdata, Socket *listen_sock,
584 		   void *sys_cldata )
585 {
586   ClientData *cldata;
587   Socket *new_socket;
588 
589   if( (new_socket = socket_accept( listen_sock )) == NULL ){
590     camserv_log( MODNAME, "Error accepting new socket!");
591     return;
592   }
593 
594   if( (cldata = client_data_new()) == NULL ){
595     camserv_log( MODNAME, "Error allocating clientdata!");
596     socket_dest( new_socket );
597     return;
598   }
599 
600   cldata->clienttype = CLIENT_T_UNK;
601   if( sock_field_manage_socket( sfdata, new_socket, cldata ) == -1 ){
602     camserv_log( MODNAME, "Error managing new socket" );
603     socket_dest( new_socket );
604     client_data_dest( cldata );
605     return;
606   }
607   camserv_log( MODNAME, "Accepted client: \"%s\"",
608 	       socket_query_remote_name( new_socket ));
609 }
610 
611 
612 static
relay_timeout(SockField_Data * sfdata,void * sys_cldata)613 void relay_timeout( SockField_Data *sfdata, void *sys_cldata ){
614   RelayData *rdata = sys_cldata;
615 
616   if( rdata->camera_sock == NULL ) {
617     camserv_log( MODNAME, "Attempting connect to camserver");
618     if( relay_connect_camserv( sfdata, rdata ) == -1 ){
619       camserv_log( MODNAME, "Error connecting to camserv: %s %d",
620 	       rdata->camera_ip, rdata->camera_port );
621     }
622   }
623 }
624 
625 
main(int argc,char * argv[])626 int main( int argc, char *argv[] ){
627   Socket *listen_sock;
628   int localport, remoteport;
629   RelayData rdata;
630   struct timeval retry_time;
631 
632   if( argc < 3 ) {
633     fprintf( stderr, "camserv relay v%s - by Jon Travis (jtravis@p00p.org)\n",
634 	     VERSION );
635     fprintf( stderr, "Syntax: %s <port> <camserv IP> <ccamserv port>\n",
636 	     argv[ 0 ]);
637     fprintf( stderr, "\tThe relay will bind port <port> on the localhost\n");
638     fprintf( stderr, "\tand connect to the camserv port to do the relay.\n");
639     return -1;
640   }
641 
642   signal( SIGPIPE, SIG_IGN );
643   if( sscanf( argv[1], "%d", &localport ) != 1 ) {
644     camserv_log( MODNAME, "Error:  port \"%s\" invalid!", argv[ 1 ] );
645     return -1;
646   }
647 
648   if( localport < 1024 )
649     camserv_log( MODNAME, "Warning:  Port numbers < 1024 shouldn't be used");
650 
651   if( sscanf( argv[3], "%d", &remoteport ) != 1 ) {
652     camserv_log( MODNAME, "Error:  camserv port \"%s\" invalid!", argv[3] );
653     return -1;
654   }
655 
656   strncpy( rdata.camera_ip, argv[2], sizeof( rdata.camera_ip ) - 1);
657   rdata.camera_ip[ sizeof( rdata.camera_ip ) - 1 ] = '\0';
658   rdata.camera_port = remoteport;
659   rdata.camera_sock = NULL;
660   if( (listen_sock = socket_serve_tcp( NULL, localport, BACKLOG )) == NULL ){
661     camserv_log( MODNAME, "Could not bind local port: %s",
662 	     strerror( errno ));
663     return -1;
664   }
665 
666   retry_time.tv_sec = 10;
667   retry_time.tv_usec = 0;
668   if( sock_field( listen_sock, &rdata, relay_init, relay_accept,
669 		  relay_client_read, relay_client_write,
670 		  relay_preclose, relay_timeout, &retry_time ) == -1 )
671   {
672     socket_dest( listen_sock );
673     camserv_log( MODNAME, "Camserv relay abnormally terminated");
674     return -1;
675   }
676   socket_dest( listen_sock );
677   return 0;
678 }
679