1 
2 /* $Id: comserv.c,v 1.27 2002/06/20 01:52:48 bsd Exp $ */
3 
4 /*
5  * Copyright 2000, 2001, 2002 Brian S. Dean <bsd@bsdhome.com>
6  * All Rights Reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY BRIAN S. DEAN ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL BRIAN S. DEAN BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29  * DAMAGE.
30  *
31  */
32 
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/ioctl.h>
40 #include <errno.h>
41 #include <netinet/in.h>
42 #include <netdb.h>
43 #include <fcntl.h>
44 
45 
46 #include "comserv.h"
47 #include "log.h"
48 #include "pty.h"
49 
50 
51 char * comserv_versionid = "$Id: comserv.c,v 1.27 2002/06/20 01:52:48 bsd Exp $";
52 
53 char config_file [ PATH_MAX ];
54 
55 
statestr(int state)56 char * statestr(int state)
57 {
58   switch(state) {
59     case ST_START      : return "START"; break;
60     case ST_INCOMING   : return "INCOMING"; break;
61     case ST_CMDINPUT   : return "CMDINPUT"; break;
62     case ST_REMOTE     : return "REMOTE"; break;
63     case ST_LOCAL      : return "LOCAL"; break;
64     case ST_CONNECTING : return "CONNECTING"; break;
65     default            : return "<unknown>"; break;
66   }
67 }
68 
69 
70 /*
71  * sock_bind_service
72  *
73  * Initialize a connection and bind to the specified service.
74  */
75 #define SBS_MAXTRIES 30
sock_bind_service(char * service,int srvport)76 int sock_bind_service(char * service, int srvport)
77 {
78   int sockfd;
79   int rc;
80   struct servent *ps;
81   struct sockaddr_in sin;
82   short port;
83   int tries=SBS_MAXTRIES;
84   int on;
85 
86   if (service) {
87     if (isdigit(service[0])) {
88       port = (short)atol(service);
89     }
90     else {
91       ps = getservbyname(service, "tcp");
92       if (ps == NULL)  {
93         msgout("cannot locate \"%s\" service, telnet port disabled\n",
94                service);
95         return -1;
96       }
97       port = ntohs(ps->s_port);
98     }
99   }
100   else {
101     port = srvport;
102   }
103 
104   sin.sin_family = AF_INET;
105   sin.sin_addr.s_addr = INADDR_ANY;
106   sin.sin_port = htons(port);
107 
108   sockfd = socket (AF_INET, SOCK_STREAM, 0);
109   if (sockfd < 0)  {
110     msgout("socket(): %s; telnet port disabled\n",
111            strerror(errno));
112     return -2;
113   }
114 
115   on = 1;
116   rc = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
117   if (rc < 0) {
118     msgout("setsockopt(SO_RESUSEADDR): %s\n", strerror(errno));
119   }
120 
121   rc = bind(sockfd, (struct sockaddr *)&sin, sizeof(sin));
122   if (rc < 0)  {
123     msgout("bind(): port=%d: %s\n", port, strerror(errno));
124     close(sockfd);
125     return -3;
126   }
127 
128   rc = listen(sockfd, 20);
129   if (rc < 0) {
130     msgout("listen(): %s; telnet port disabled\n", strerror(errno));
131     close(sockfd);
132     return -4;
133   }
134 
135   if (service)
136     msgout("comservd listening on service \"%s\"\n", service);
137   else
138     msgout("comservd listening on port %d\n", srvport);
139 
140   return sockfd;
141 }
142 
143 
144 
145 /*
146  * sock_bind_connect
147  *
148  * Open a connection to the specified host and service.
149  */
sock_bind_connect(char * host,char * service,int srvport,int * ready)150 int sock_bind_connect(char * host, char * service, int srvport, int * ready)
151 {
152   int sockfd;
153   int rc;
154   struct hostent * h;
155   struct servent * ps;
156   struct sockaddr_in lh; /* local host */
157   short lp;              /* local port */
158   struct sockaddr_in rh; /* remote host */
159   short rp;              /* remote port */
160   int ioc;
161 
162   *ready = 0;
163 
164   if (service) {
165     if (isdigit(service[0])) {
166       rp = (short)atol(service);
167     }
168     else {
169       ps = getservbyname(service, "tcp");
170       if (ps == NULL)  {
171         msgout("cannot get %s service\n", service);
172         exit (1);
173       }
174       rp = ntohs(ps->s_port);
175     }
176   }
177   else {
178     rp = srvport;
179   }
180 
181   lp                 = 0; /* use an ephemeral port */
182   lh.sin_family      = AF_INET;
183   lh.sin_addr.s_addr = INADDR_ANY;
184   lh.sin_port        = htons(lp);
185 
186 
187   h = gethostbyname(host);
188   if (h == NULL) {
189     msgout("unknown host %s\n", host);
190     return -1;
191   }
192 
193   rh.sin_family      = AF_INET;
194   rh.sin_addr.s_addr = *(unsigned int *)h->h_addr_list[0];
195   rh.sin_port        = htons(rp);
196 
197   if (DEBUG_CONNECT()) {
198     msgout("host %s is 0x%08x\n", host, rh.sin_addr.s_addr);
199   }
200 
201   sockfd = socket (AF_INET, SOCK_STREAM, 0);
202   if (sockfd < 0)  {
203     msgout("socket(): %s\n", strerror(errno));
204     return -2;
205   }
206 
207   rc = bind(sockfd, (struct sockaddr *)&lh, sizeof(lh));
208   if (rc < 0)  {
209     msgout("bind(host=0x%08lx,port=%d): %s\n",
210            ntohl(lh.sin_addr.s_addr), ntohs(lh.sin_port),
211            strerror(errno));
212     close(sockfd);
213     return -4;
214   }
215 
216   /*
217    * set the socket to non-blocking so the connect doesn't wait
218    * forever if the terminal server is down.
219    */
220   ioc = 1;
221   rc = ioctl(sockfd, FIONBIO, &ioc);
222   if (rc < 0) {
223     msgout("ioctl(fd=%d, FIONBIO, 1): %s\n",
224            sockfd, strerror(errno));
225     close(sockfd);
226     return -3;
227   }
228 
229   *ready = 1;
230 
231   rc = connect(sockfd, (struct sockaddr *)&rh, sizeof(rh));
232   if (rc < 0) {
233     if (errno == EINPROGRESS) {
234       *ready = 0;
235     }
236     else {
237       if (DEBUG_CONNECT()) {
238         msgout("connect(host=0x%08lx,port=%d): %s\n",
239                ntohl(rh.sin_addr.s_addr), ntohs(rh.sin_port),
240                strerror(errno));
241       }
242       close(sockfd);
243       return -5;
244     }
245   }
246 
247   return sockfd;
248 }
249 
250 
251 
connect_server(COMSERV_PORT * xp)252 int connect_server(COMSERV_PORT * xp)
253 {
254   int fd;
255   int ready;
256 
257   /*
258    * initialize REMOTE side of the connection
259    */
260   if (DEBUG_CONNECT()) {
261     msgout("establishing connection to %s:%d (serial port %d)\n",
262            xp->host, xp->port, xp->serport);
263   }
264 
265   xp->re = NULL;
266 
267   fd = sock_bind_connect(xp->host, NULL, xp->port, &ready);
268   if (fd < 0) {
269     if (DEBUG_CONNECT()) {
270       msgout("can't open connection to %s TCP/IP port %d "
271              "(serial port %d)\n",
272              xp->host, xp->port, xp->serport);
273       }
274     return -1;
275   }
276 
277   if (fd >= FD_SETSIZE) {
278     msgout("connect_server(): possible descriptor leak; "
279            "fd=%d has exceeded FD_SETSIZE\n",
280            fd);
281     abort();
282   }
283 
284   if (endpoint[fd] == NULL)
285     endpoint[fd] = new_endp();
286   else
287     init_endp(endpoint[fd]);
288 
289   endpoint[fd]->fd            = fd;
290   xp->re                      = endpoint[fd];
291   endpoint[fd]->state         = ST_REMOTE;
292   endpoint[fd]->comserv       = xp;
293   if (DEBUG_BUFFER())
294     msgout("connect_server(): enabling %s.rd\n",
295            whichside(endpoint[fd]->state));
296   ENABLE_RD(fd);
297 
298   /*
299    * if the connection did not complete immediately, select the socket
300    * for writability and adjust the state to reflect that it is in the
301    * process of connecting
302    */
303   if (!ready) {
304     DISABLE_RD(fd);
305     ENABLE_WR(fd);
306     endpoint[fd]->state = ST_CONNECTING;
307   }
308   else {
309     /*
310      * if we have a connection to the other side and it has data to
311      * write to us, select ourselves for writeability
312      */
313     if (xp->le && xp->le->bufcnt) {
314       if (DEBUG_BUFFER())
315         msgout("connect_server(): enabling %s.wr\n",
316                whichside(endpoint[fd]->state));
317       ENABLE_WR(fd);
318     }
319 
320     xp->reconnect_time_incr = 1;
321     xp->reconnect_time = time(NULL) + xp->reconnect_time_incr;
322   }
323 
324   maxfd = max(fd,maxfd);
325 
326   return fd;
327 }
328 
329 
330 
331 
connect_user(COMSERV_PORT * xp,int doretry)332 int connect_user(COMSERV_PORT * xp, int doretry)
333 {
334   int rc;
335   int fd;
336   int bank, unit;
337   char tty [ 32 ];
338 
339   /*
340    * initialize LOCAL side of the connection
341    */
342 
343   if (DEBUG_CONNECT())
344     msgout("configuring %s port %d on %s\n",
345            xp->host, xp->port, xp->localpath);
346 
347   if ((xp->rflags & RFLAG_LISTEN) == 0) {
348     fd = allocate_pty(&bank, &unit);
349     if (fd < 0) {
350       msgout("out of ptys\n");
351       exit(1);
352     }
353 
354     strncpy(tty, "/dev/ttyp0", sizeof(tty)-1);
355     tty[8] = bank;
356     tty[9] = unit;
357     unlink(xp->localpath);
358     rc = symlink(tty, xp->localpath);
359     if (rc < 0) {
360       msgout("can't create symbolic link from %s to %s: %s\n",
361              xp->localpath, tty, strerror(errno));
362       exit(1);
363     }
364   }
365   else {
366     fd = open(xp->localpath, O_RDWR);
367     if (fd < 0) {
368       msgout("open(\"%s\"): %s\n", xp->localpath, strerror(errno));
369       msgout("terminating\n");
370       exit(1);
371     }
372   }
373 
374   /*
375    * initialize the user side of the connection
376    */
377 
378   if (fd >= FD_SETSIZE) {
379     msgout("connect_user(): possible descriptor leak; "
380            "fd=%d has exceeded FD_SETSIZE\n",
381            fd);
382     abort();
383   }
384 
385   if (endpoint[fd] == NULL)
386     endpoint[fd] = new_endp();
387   else
388     init_endp(endpoint[fd]);
389 
390   endpoint[fd]->fd            = fd;
391   endpoint[fd]->state         = ST_LOCAL;
392   endpoint[fd]->comserv       = xp;
393   xp->le                      = endpoint[fd];
394   set_blocking(endpoint[fd], 0, doretry);
395   if (DEBUG_BUFFER())
396     msgout("connect_user(): enabling %s.rd\n",
397            whichside(endpoint[fd]->state));
398   ENABLE_RD(fd);
399 
400   if (xp->rflags & RFLAG_LISTEN) {
401     rc = endp_attr(endpoint[fd]);
402     if (rc != 0) {
403       msgout("connect_user(): endp_attr(): %s\n",
404              strerror(-rc));
405     }
406   }
407 
408   /*
409    * if we have a connection to the other side and it has data to
410    * write to use, select ourselves for writeability
411    */
412   if (xp->re && xp->re->bufcnt) {
413     msgout("connect_user(): enabling %s.wr\n",
414            whichside(endpoint[fd]->state));
415     ENABLE_WR(fd);
416   }
417 
418   maxfd = max(fd,maxfd);
419 
420   if (xp->control) {
421     rc = make_ctl_port(xp);
422     if (rc < 0) {
423       msgout("connect_user(): can't initialize control port, rc=%d\n", rc);
424       CLEANUP(xp->le);
425       return -1;
426     }
427   }
428 
429   return fd;
430 }
431 
432 
433 
434 
435 
reconnect(int state,COMSERV_PORT * xp,int dopeer)436 void reconnect(int state, COMSERV_PORT * xp, int dopeer)
437 {
438   int fd;
439   int peer;
440 
441   if (DEBUG_CONNECT()) {
442     msgout("re-establishing %s side of %s\n",
443            whichside(state), xp->localpath);
444   }
445 
446   switch (state) {
447     case ST_LOCAL    :
448     case ST_CMDINPUT :
449       fd = connect_user(xp, 1);
450       if (fd < 0) {
451         msgout("can't establish user connection to %s\n", xp->localpath);
452         break;
453       }
454       else {
455         if (dopeer && !xp->control) {
456           /* the control port doesn't have a peer connection */
457           if (xp->re) {
458             peer = xp->re->state;
459             CLEANUP(xp->re);
460           }
461           reconnect(ST_REMOTE, xp, 0);
462         }
463       }
464       break;
465 
466     case ST_REMOTE :
467       if ((xp->rflags & RFLAG_LISTEN) == 0) {
468         /* try to connect right now */
469         fd = connect_server(xp);
470         if (fd) {
471           if (dopeer) {
472             /*
473              * only attempt a reconnect if the local side was not
474              * already connected
475              */
476             if (!xp->le) {
477               reconnect(ST_LOCAL, xp, 0);
478             }
479           }
480         }
481       }
482       break;
483 
484     default :
485       msgout("reconnect(): invalid state=%d\n", state);
486       exit(1);
487       break;
488   }
489 }
490 
491 
492 
make_ctl_port(COMSERV_PORT * xp)493 int make_ctl_port(COMSERV_PORT * xp)
494 {
495   int rc;
496 
497   /* set this if not already set */
498   xp->control = 1;
499 
500   rc = set_command(xp->le);
501   if (rc)
502     return -1;
503 
504   xp->flags             = OPT_WAIT;
505   xp->re                = NULL;
506   xp->le->state         = ST_CMDINPUT;
507   xp->le->command->echo = 1;
508   xp->le->command->h    = NULL;
509   snprintf(xp->le->command->name, MAX_HOST, "control");
510 
511   return 0;
512 }
513 
514 
new_comserv_port(void)515 COMSERV_PORT * new_comserv_port(void)
516 {
517   COMSERV_PORT * xp;
518 
519   xp = (COMSERV_PORT *) malloc(sizeof(COMSERV_PORT));
520   if (xp == NULL) {
521     msgout("out of memory allocating COMSERV_PORT\n");
522     exit(1);
523   }
524 
525   bzero(xp, sizeof(*xp));
526 
527   return xp;
528 }
529 
530 
531 
free_comserv_port(COMSERV_PORT * xp)532 void free_comserv_port(COMSERV_PORT * xp)
533 {
534   COMSERV_PORT * xp1, * xp2;
535 
536   /*
537    * unlink xp from the list of device nodes
538    */
539   xp1 = comserv_ports;
540   xp2 = NULL;
541   while (xp1) {
542     if (xp1 == xp) {
543       if (xp2) {
544         /* not first in the list */
545         xp2->next = xp1->next;
546       }
547       else {
548         /* first in the list */
549         comserv_ports = xp1->next;
550       }
551       break;
552     }
553     xp2 = xp1;
554     xp1 = xp1->next;
555   }
556 
557   if (xp->le) {
558     CLEANUP(xp->le);
559   }
560   if (xp->re) {
561     CLEANUP(xp->re);
562   }
563   if (xp->listen) {
564     CLEANUP(xp->listen);
565   }
566 
567   if (xp->log)
568     close(xp->logfd);
569 
570   free(xp);
571 }
572 
573