blob: 207732b72562a6c09d45f7240d7428e16eb3d00e [file] [log] [blame]
Eric Andersen28c70b32000-06-14 20:42:57 +00001/* vi: set sw=4 ts=4: */
Erik Andersenf7c49ef2000-02-22 17:17:45 +00002/*
Eric Andersen28c70b32000-06-14 20:42:57 +00003 * telnet implementation for busybox
Erik Andersenf7c49ef2000-02-22 17:17:45 +00004 *
Eric Andersen28c70b32000-06-14 20:42:57 +00005 * Author: Tomi Ollila <too@iki.fi>
6 * Copyright (C) 1994-2000 by Tomi Ollila
7 *
8 * Created: Thu Apr 7 13:29:41 1994 too
9 * Last modified: Fri Jun 9 14:34:24 2000 too
Erik Andersenf7c49ef2000-02-22 17:17:45 +000010 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
Eric Andersen28c70b32000-06-14 20:42:57 +000025 * HISTORY
26 * Revision 3.1 1994/04/17 11:31:54 too
27 * initial revision
28 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen
29 * <andersen@lineo.com>
Eric Andersen7e1273e2001-05-07 17:57:45 +000030 * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
31 * <jam@ltsp.org>
Erik Andersenf7c49ef2000-02-22 17:17:45 +000032 *
Erik Andersenf7c49ef2000-02-22 17:17:45 +000033 */
34
Erik Andersenf7c49ef2000-02-22 17:17:45 +000035#include <termios.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000036#include <unistd.h>
37#include <errno.h>
38#include <stdlib.h>
39#include <stdarg.h>
40#include <string.h>
41#include <signal.h>
42#include <arpa/telnet.h>
Erik Andersenf7c49ef2000-02-22 17:17:45 +000043#include <sys/types.h>
44#include <sys/socket.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000045#include <netinet/in.h>
46#include <netdb.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000047#include "busybox.h"
Erik Andersenf7c49ef2000-02-22 17:17:45 +000048
Eric Andersen28c70b32000-06-14 20:42:57 +000049#if 0
Mark Whitley59ab0252001-01-23 22:30:04 +000050static const int DOTRACE = 1;
Eric Andersen28c70b32000-06-14 20:42:57 +000051#endif
52
Pavel Roskin616d13b2000-07-28 19:38:27 +000053#ifdef DOTRACE
Eric Andersen28c70b32000-06-14 20:42:57 +000054#include <arpa/inet.h> /* for inet_ntoa()... */
55#define TRACE(x, y) do { if (x) printf y; } while (0)
56#else
57#define TRACE(x, y)
58#endif
59
60#if 0
61#define USE_POLL
62#include <sys/poll.h>
63#else
64#include <sys/time.h>
65#endif
66
Mark Whitley59ab0252001-01-23 22:30:04 +000067static const int DATABUFSIZE = 128;
68static const int IACBUFSIZE = 128;
Eric Andersen28c70b32000-06-14 20:42:57 +000069
Mark Whitley59ab0252001-01-23 22:30:04 +000070static const int CHM_TRY = 0;
71static const int CHM_ON = 1;
72static const int CHM_OFF = 2;
Eric Andersen28c70b32000-06-14 20:42:57 +000073
Mark Whitley59ab0252001-01-23 22:30:04 +000074static const int UF_ECHO = 0x01;
75static const int UF_SGA = 0x02;
Eric Andersen28c70b32000-06-14 20:42:57 +000076
Mark Whitley59ab0252001-01-23 22:30:04 +000077enum {
78 TS_0 = 1,
79 TS_IAC = 2,
80 TS_OPT = 3,
81 TS_SUB1 = 4,
82 TS_SUB2 = 5,
83};
Eric Andersen28c70b32000-06-14 20:42:57 +000084
85#define WriteCS(fd, str) write(fd, str, sizeof str -1)
86
87typedef unsigned char byte;
88
89/* use globals to reduce size ??? */ /* test this hypothesis later */
Eric Andersen92d23242001-03-19 23:49:41 +000090static struct Globalvars {
Eric Andersen28c70b32000-06-14 20:42:57 +000091 int netfd; /* console fd:s are 0 and 1 (and 2) */
92 /* same buffer used both for network and console read/write */
93 char * buf; /* allocating so static size is smaller */
94 short len;
95 byte telstate; /* telnet negotiation state from network input */
96 byte telwish; /* DO, DONT, WILL, WONT */
97 byte charmode;
98 byte telflags;
99 byte gotsig;
100 /* buffer to handle telnet negotiations */
101 char * iacbuf;
102 short iaclen; /* could even use byte */
103 struct termios termios_def;
104 struct termios termios_raw;
105} G;
106
107#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
108
109#ifdef USE_GLOBALVAR_PTR
110struct Globalvars * Gptr;
111#define G (*Gptr)
112#else
Eric Andersen92d23242001-03-19 23:49:41 +0000113static struct Globalvars G;
Eric Andersen28c70b32000-06-14 20:42:57 +0000114#endif
115
116static inline void iacflush()
117{
118 write(G.netfd, G.iacbuf, G.iaclen);
119 G.iaclen = 0;
120}
121
122/* Function prototypes */
123static int getport(char * p);
124static struct in_addr getserver(char * p);
125static int create_socket();
126static void setup_sockaddr_in(struct sockaddr_in * addr, int port);
127static int remote_connect(struct in_addr addr, int port);
128static void rawmode();
129static void cookmode();
130static void do_linemode();
131static void will_charmode();
132static void telopt(byte c);
133static int subneg(byte c);
134#if 0
135static int local_bind(int port);
136#endif
137
138/* Some globals */
139static int one = 1;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000140
Eric Andersen7e1273e2001-05-07 17:57:45 +0000141#ifdef BB_FEATURE_TELNET_TTYPE
142static char *ttype;
143#endif
144
Eric Andersen28c70b32000-06-14 20:42:57 +0000145static void doexit(int ev)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000146{
Eric Andersen28c70b32000-06-14 20:42:57 +0000147 cookmode();
148 exit(ev);
149}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000150
Eric Andersen28c70b32000-06-14 20:42:57 +0000151static void conescape()
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000152{
Eric Andersen28c70b32000-06-14 20:42:57 +0000153 char b;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000154
Eric Andersen28c70b32000-06-14 20:42:57 +0000155 if (G.gotsig) /* came from line mode... go raw */
156 rawmode();
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000157
Eric Andersen28c70b32000-06-14 20:42:57 +0000158 WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
159 " l go to line mode\r\n"
160 " c go to character mode\r\n"
161 " z suspend telnet\r\n"
162 " e exit telnet\r\n");
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000163
Eric Andersen28c70b32000-06-14 20:42:57 +0000164 if (read(0, &b, 1) <= 0)
165 doexit(1);
166
167 switch (b)
168 {
169 case 'l':
170 if (!G.gotsig)
171 {
172 do_linemode();
173 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000174 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000175 break;
176 case 'c':
177 if (G.gotsig)
178 {
179 will_charmode();
180 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000181 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000182 break;
183 case 'z':
184 cookmode();
185 kill(0, SIGTSTP);
186 rawmode();
187 break;
188 case 'e':
189 doexit(0);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000190 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000191
192 WriteCS(1, "continuing...\r\n");
193
194 if (G.gotsig)
195 cookmode();
196
197 rrturn:
198 G.gotsig = 0;
199
200}
201static void handlenetoutput()
202{
203 /* here we could do smart tricks how to handle 0xFF:s in output
204 * stream like writing twice every sequence of FF:s (thus doing
205 * many write()s. But I think interactive telnet application does
206 * not need to be 100% 8-bit clean, so changing every 0xff:s to
207 * 0x7f:s */
208
209 int i;
210 byte * p = G.buf;
211
212 for (i = G.len; i > 0; i--, p++)
213 {
214 if (*p == 0x1d)
215 {
216 conescape();
217 return;
218 }
219 if (*p == 0xff)
220 *p = 0x7f;
221 }
222 write(G.netfd, G.buf, G.len);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000223}
224
Eric Andersen28c70b32000-06-14 20:42:57 +0000225
226static void handlenetinput()
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000227{
Eric Andersen28c70b32000-06-14 20:42:57 +0000228 int i;
229 int cstart = 0;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000230
Eric Andersen28c70b32000-06-14 20:42:57 +0000231 for (i = 0; i < G.len; i++)
232 {
233 byte c = G.buf[i];
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000234
Eric Andersen28c70b32000-06-14 20:42:57 +0000235 if (G.telstate == 0) /* most of the time state == 0 */
236 {
237 if (c == IAC)
238 {
239 cstart = i;
240 G.telstate = TS_IAC;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000241 }
242 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000243 else
244 switch (G.telstate)
245 {
246 case TS_0:
247 if (c == IAC)
248 G.telstate = TS_IAC;
249 else
250 G.buf[cstart++] = c;
251 break;
252
253 case TS_IAC:
254 if (c == IAC) /* IAC IAC -> 0xFF */
255 {
256 G.buf[cstart++] = c;
257 G.telstate = TS_0;
258 break;
259 }
260 /* else */
261 switch (c)
262 {
263 case SB:
264 G.telstate = TS_SUB1;
265 break;
266 case DO:
267 case DONT:
268 case WILL:
269 case WONT:
270 G.telwish = c;
271 G.telstate = TS_OPT;
272 break;
273 default:
274 G.telstate = TS_0; /* DATA MARK must be added later */
275 }
276 break;
277 case TS_OPT: /* WILL, WONT, DO, DONT */
278 telopt(c);
279 G.telstate = TS_0;
280 break;
281 case TS_SUB1: /* Subnegotiation */
282 case TS_SUB2: /* Subnegotiation */
283 if (subneg(c) == TRUE)
284 G.telstate = TS_0;
285 break;
286 }
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000287 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000288 if (G.telstate)
289 {
290 if (G.iaclen) iacflush();
291 if (G.telstate == TS_0) G.telstate = 0;
292
293 G.len = cstart;
294 }
295
296 if (G.len)
297 write(1, G.buf, G.len);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000298}
299
Eric Andersen28c70b32000-06-14 20:42:57 +0000300
301/* ******************************* */
302
303static inline void putiac(int c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000304{
Eric Andersen28c70b32000-06-14 20:42:57 +0000305 G.iacbuf[G.iaclen++] = c;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000306}
307
Eric Andersen28c70b32000-06-14 20:42:57 +0000308
309static void putiac2(byte wwdd, byte c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000310{
Eric Andersen28c70b32000-06-14 20:42:57 +0000311 if (G.iaclen + 3 > IACBUFSIZE)
312 iacflush();
313
314 putiac(IAC);
315 putiac(wwdd);
316 putiac(c);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000317}
318
Eric Andersen28c70b32000-06-14 20:42:57 +0000319#if 0
320static void putiac1(byte c)
321{
322 if (G.iaclen + 2 > IACBUFSIZE)
323 iacflush();
324
325 putiac(IAC);
326 putiac(c);
327}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000328#endif
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000329
Eric Andersen7e1273e2001-05-07 17:57:45 +0000330#ifdef BB_FEATURE_TELNET_TTYPE
331static void putiac_subopt(byte c, char *str)
332{
333 int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
334
335 if (G.iaclen + len > IACBUFSIZE)
336 iacflush();
337
338 putiac(IAC);
339 putiac(SB);
340 putiac(c);
341 putiac(0);
342
343 while(*str)
344 putiac(*str++);
345
346 putiac(IAC);
347 putiac(SE);
348}
349#endif
350
Eric Andersen28c70b32000-06-14 20:42:57 +0000351/* void putiacstring (subneg strings) */
352
353/* ******************************* */
354
Eric Andersen3e6ff902001-03-09 21:24:12 +0000355static char const escapecharis[] = "\r\nEscape character is ";
Eric Andersen28c70b32000-06-14 20:42:57 +0000356
357static void setConMode()
358{
359 if (G.telflags & UF_ECHO)
360 {
361 if (G.charmode == CHM_TRY) {
362 G.charmode = CHM_ON;
Matt Kraai12f417e2001-01-18 02:57:08 +0000363 printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000364 rawmode();
365 }
366 }
367 else
368 {
369 if (G.charmode != CHM_OFF) {
370 G.charmode = CHM_OFF;
Matt Kraai12f417e2001-01-18 02:57:08 +0000371 printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000372 cookmode();
373 }
374 }
375}
376
377/* ******************************* */
378
379static void will_charmode()
380{
381 G.charmode = CHM_TRY;
382 G.telflags |= (UF_ECHO | UF_SGA);
383 setConMode();
384
385 putiac2(DO, TELOPT_ECHO);
386 putiac2(DO, TELOPT_SGA);
387 iacflush();
388}
389
390static void do_linemode()
391{
392 G.charmode = CHM_TRY;
393 G.telflags &= ~(UF_ECHO | UF_SGA);
394 setConMode();
395
396 putiac2(DONT, TELOPT_ECHO);
397 putiac2(DONT, TELOPT_SGA);
398 iacflush();
399}
400
401/* ******************************* */
402
403static inline void to_notsup(char c)
404{
405 if (G.telwish == WILL) putiac2(DONT, c);
406 else if (G.telwish == DO) putiac2(WONT, c);
407}
408
409static inline void to_echo()
410{
411 /* if server requests ECHO, don't agree */
412 if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
413 else if (G.telwish == DONT) return;
414
415 if (G.telflags & UF_ECHO)
416 {
417 if (G.telwish == WILL)
418 return;
419 }
420 else
421 if (G.telwish == WONT)
422 return;
423
424 if (G.charmode != CHM_OFF)
425 G.telflags ^= UF_ECHO;
426
427 if (G.telflags & UF_ECHO)
428 putiac2(DO, TELOPT_ECHO);
429 else
430 putiac2(DONT, TELOPT_ECHO);
431
432 setConMode();
433 WriteCS(1, "\r\n"); /* sudden modec */
434}
435
436static inline void to_sga()
437{
438 /* daemon always sends will/wont, client do/dont */
439
440 if (G.telflags & UF_SGA)
441 {
442 if (G.telwish == WILL)
443 return;
444 }
445 else
446 if (G.telwish == WONT)
447 return;
448
449 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
450 putiac2(DO, TELOPT_SGA);
451 else
452 putiac2(DONT, TELOPT_SGA);
453
454 return;
455}
456
Eric Andersen7e1273e2001-05-07 17:57:45 +0000457#ifdef BB_FEATURE_TELNET_TTYPE
458static inline void to_ttype()
459{
460 /* Tell server we will (or won't) do TTYPE */
461
462 if(ttype)
463 putiac2(WILL, TELOPT_TTYPE);
464 else
465 putiac2(WONT, TELOPT_TTYPE);
466
467 return;
468}
469#endif
470
Eric Andersen28c70b32000-06-14 20:42:57 +0000471static void telopt(byte c)
472{
473 switch (c)
474 {
475 case TELOPT_ECHO: to_echo(c); break;
476 case TELOPT_SGA: to_sga(c); break;
Eric Andersen7e1273e2001-05-07 17:57:45 +0000477#ifdef BB_FEATURE_TELNET_TTYPE
478 case TELOPT_TTYPE: to_ttype(c); break;
479#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000480 default: to_notsup(c); break;
481 }
482}
483
484
485/* ******************************* */
486
Eric Andersen7e1273e2001-05-07 17:57:45 +0000487/* subnegotiation -- ignore all (except TTYPE) */
Eric Andersen28c70b32000-06-14 20:42:57 +0000488
489static int subneg(byte c)
490{
491 switch (G.telstate)
492 {
493 case TS_SUB1:
494 if (c == IAC)
495 G.telstate = TS_SUB2;
Eric Andersen7e1273e2001-05-07 17:57:45 +0000496#ifdef BB_FEATURE_TELNET_TTYPE
497 else
498 if (c == TELOPT_TTYPE)
499 putiac_subopt(TELOPT_TTYPE,ttype);
500#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000501 break;
502 case TS_SUB2:
503 if (c == SE)
504 return TRUE;
505 G.telstate = TS_SUB1;
506 /* break; */
507 }
508 return FALSE;
509}
510
511/* ******************************* */
512
513static void fgotsig(int sig)
514{
515 G.gotsig = sig;
516}
517
518
519static void rawmode()
520{
521 tcsetattr(0, TCSADRAIN, &G.termios_raw);
522}
523
524static void cookmode()
525{
526 tcsetattr(0, TCSADRAIN, &G.termios_def);
527}
528
529extern int telnet_main(int argc, char** argv)
530{
531 struct in_addr host;
532 int port;
533#ifdef USE_POLL
534 struct pollfd ufds[2];
535#else
536 fd_set readfds;
537 int maxfd;
538#endif
539
Eric Andersen7e1273e2001-05-07 17:57:45 +0000540#ifdef BB_FEATURE_TELNET_TTYPE
541 ttype = getenv("TERM");
542#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000543
544 memset(&G, 0, sizeof G);
545
546 if (tcgetattr(0, &G.termios_def) < 0)
547 exit(1);
548
549 G.termios_raw = G.termios_def;
550
551 cfmakeraw(&G.termios_raw);
552
Eric Andersen67991cf2001-02-14 21:23:06 +0000553 if (argc < 2) show_usage();
Eric Andersen28c70b32000-06-14 20:42:57 +0000554 port = (argc > 2)? getport(argv[2]): 23;
555
556 G.buf = xmalloc(DATABUFSIZE);
557 G.iacbuf = xmalloc(IACBUFSIZE);
558
559 host = getserver(argv[1]);
560
561 G.netfd = remote_connect(host, port);
562
563 signal(SIGINT, fgotsig);
564
565#ifdef USE_POLL
566 ufds[0].fd = 0; ufds[1].fd = G.netfd;
567 ufds[0].events = ufds[1].events = POLLIN;
568#else
569 FD_ZERO(&readfds);
570 FD_SET(0, &readfds);
571 FD_SET(G.netfd, &readfds);
572 maxfd = G.netfd + 1;
573#endif
574
575 while (1)
576 {
577#ifndef USE_POLL
578 fd_set rfds = readfds;
579
580 switch (select(maxfd, &rfds, NULL, NULL, NULL))
581#else
582 switch (poll(ufds, 2, -1))
583#endif
584 {
585 case 0:
586 /* timeout */
587 case -1:
588 /* error, ignore and/or log something, bay go to loop */
589 if (G.gotsig)
590 conescape();
591 else
592 sleep(1);
593 break;
594 default:
595
596#ifdef USE_POLL
597 if (ufds[0].revents) /* well, should check POLLIN, but ... */
598#else
599 if (FD_ISSET(0, &rfds))
600#endif
601 {
602 G.len = read(0, G.buf, DATABUFSIZE);
603
604 if (G.len <= 0)
605 doexit(0);
606
607 TRACE(0, ("Read con: %d\n", G.len));
608
609 handlenetoutput();
610 }
611
612#ifdef USE_POLL
613 if (ufds[1].revents) /* well, should check POLLIN, but ... */
614#else
615 if (FD_ISSET(G.netfd, &rfds))
616#endif
617 {
618 G.len = read(G.netfd, G.buf, DATABUFSIZE);
619
620 if (G.len <= 0)
621 {
622 WriteCS(1, "Connection closed by foreign host.\r\n");
623 doexit(1);
624 }
625 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, G.len));
626
627 handlenetinput();
628 }
629 }
630 }
631}
632
633static int getport(char * p)
634{
635 unsigned int port = atoi(p);
636
637 if ((unsigned)(port - 1 ) > 65534)
638 {
Matt Kraaidd19c692001-01-31 19:00:21 +0000639 error_msg_and_die("%s: bad port number", p);
Eric Andersen28c70b32000-06-14 20:42:57 +0000640 }
641 return port;
642}
643
644static struct in_addr getserver(char * host)
645{
646 struct in_addr addr;
647
648 struct hostent * he;
649 if ((he = gethostbyname(host)) == NULL)
650 {
Matt Kraaidd19c692001-01-31 19:00:21 +0000651 error_msg_and_die("%s: Unknown host", host);
Eric Andersen28c70b32000-06-14 20:42:57 +0000652 }
653 memcpy(&addr, he->h_addr, sizeof addr);
654
655 TRACE(1, ("addr: %s\n", inet_ntoa(addr)));
656
657 return addr;
658}
659
660static int create_socket()
661{
662 return socket(AF_INET, SOCK_STREAM, 0);
663}
664
665static void setup_sockaddr_in(struct sockaddr_in * addr, int port)
666{
Eric Andersen8b960322001-03-17 05:43:39 +0000667 memset(addr, 0, sizeof(struct sockaddr_in));
Eric Andersen28c70b32000-06-14 20:42:57 +0000668 addr->sin_family = AF_INET;
669 addr->sin_port = htons(port);
670}
671
672#if 0
673static int local_bind(int port)
674{
675 struct sockaddr_in s_addr;
676 int s = create_socket();
677
678 setup_sockaddr_in(&s_addr, port);
679
680 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
681
682 if (bind(s, &s_addr, sizeof s_addr) < 0)
683 {
684 char * e = sys_errlist[errno];
685 syserrorexit("bind");
686 exit(1);
687 }
688 listen(s, 1);
689
690 return s;
691}
692#endif
693
694static int remote_connect(struct in_addr addr, int port)
695{
696 struct sockaddr_in s_addr;
697 int s = create_socket();
698
699 setup_sockaddr_in(&s_addr, port);
700 s_addr.sin_addr = addr;
701
702 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
703
704 if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0)
705 {
Matt Kraai1fa1ade2000-12-18 03:57:16 +0000706 perror_msg_and_die("Unable to connect to remote host");
Eric Andersen28c70b32000-06-14 20:42:57 +0000707 }
708 return s;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000709}
710
711/*
Eric Andersen28c70b32000-06-14 20:42:57 +0000712Local Variables:
713c-file-style: "linux"
714c-basic-offset: 4
715tab-width: 4
716End:
717*/
718