1: /* mini_sendmail - accept email on behalf of real sendmail
2: **
3: ** Copyright © 1999 by Jef Poskanzer <jef@mail.acme.com>.
4: ** All rights reserved.
5: **
6: ** Redistribution and use in source and binary forms, with or without
7: ** modification, are permitted provided that the following conditions
8: ** are met:
9: ** 1. Redistributions of source code must retain the above copyright
10: ** notice, this list of conditions and the following disclaimer.
11: ** 2. Redistributions in binary form must reproduce the above copyright
12: ** notice, this list of conditions and the following disclaimer in the
13: ** documentation and/or other materials provided with the distribution.
14: **
15: ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16: ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17: ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18: ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19: ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20: ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21: ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22: ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23: ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24: ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25: ** SUCH DAMAGE.
26: */
27:
28:
29: /* These defines control some optional features of the program. If you
30: ** don't want the features you can undef the symbols; some of them mean
31: ** a significant savings in executable size.
32: */
33: #define DO_RECEIVED /* whether to add a "Received:" header */
34: #define DO_GETPWUID /* whether to try a getpwuid() if getlogin() fails */
35: #define DO_MINUS_SP /* whether to implement the -s and -p flags */
36: #define DO_DNS /* whether to do a name lookup on -s, or just IP# */
37:
38:
39: #include <unistd.h>
40: #include <stdlib.h>
41: #include <stdio.h>
42: #include <signal.h>
43: #include <string.h>
44: #include <sys/types.h>
45: #include <sys/socket.h>
46: #include <netinet/in.h>
47: #include <netdb.h>
48:
49: #ifdef DO_RECEIVED
50: #include <time.h>
51: #endif /* DO_RECEIVED */
52: #ifdef DO_GETPWUID
53: #include <pwd.h>
54: #endif /* DO_GETPWUID */
55:
56: #include "version.h"
57:
58:
59: /* Defines. */
60: #define SMTP_PORT 25
61: #define DEFAULT_TIMEOUT 60
62:
63:
64: /* Globals. */
65: static char* argv0;
66: static char* fake_from;
67: static int parse_message, verbose;
68: #ifdef DO_MINUS_SP
69: static char* server;
70: static short port;
71: #endif /* DO_MINUS_SP */
72: static int timeout;
73: static int sockfd1, sockfd2;
74: static FILE* sockrfp;
75: static FILE* sockwfp;
76: static int got_a_recipient;
77:
78:
79: /* Forwards. */
80: static void usage( void );
81: static char* slurp_message( void );
82: #ifdef DO_RECEIVED
83: static char* make_received( char* from, char* username, char* hostname );
84: #endif /* DO_RECEIVED */
85: static void parse_for_recipients( char* message );
86: static void add_recipient( char* recipient, int len );
87: static int open_client_socket( void );
88: static int read_response( void );
89: static void send_command( char* command );
90: static void send_data( char* data );
91: static void send_done( void );
92: static void sigcatch( int sig );
93: static void show_error( char* cause );
94:
95:
96: int
97: main( int argc, char** argv )
98: {
99: int argn;
100: char* message;
101: #ifdef DO_RECEIVED
102: char* received;
103: #endif /* DO_RECEIVED */
104: char* username;
105: char hostname[500];
106: char from[1000];
107: int status;
108: char buf[2000];
109:
110: /* Parse args. */
111: argv0 = argv[0];
112: fake_from = (char*) 0;
113: parse_message = 0;
114: #ifdef DO_MINUS_SP
115: server = "127.0.0.1";
116: port = SMTP_PORT;
117: #endif /* DO_MINUS_SP */
118: verbose = 0;
119: timeout = DEFAULT_TIMEOUT;
120: argn = 1;
121: while ( argn < argc && argv[argn][0] == '-' )
122: {
123: if ( strncmp( argv[argn], "-f", 2 ) == 0 && argv[argn][2] != '\0' )
124: fake_from = &(argv[argn][2]);
125: else if ( strcmp( argv[argn], "-t" ) == 0 )
126: parse_message = 1;
127: #ifdef DO_MINUS_SP
128: else if ( strncmp( argv[argn], "-s", 2 ) == 0 && argv[argn][2] != '\0' )
129: server = &(argv[argn][2]);
130: else if ( strncmp( argv[argn], "-p", 2 ) == 0 && argv[argn][2] != '\0' )
131: port = atoi( &(argv[argn][2]) );
132: #endif /* DO_MINUS_SP */
133: else if ( strncmp( argv[argn], "-T", 2 ) == 0 && argv[argn][2] != '\0' )
134: timeout = atoi( &(argv[argn][2]) );
135: else if ( strcmp( argv[argn], "-v" ) == 0 )
136: verbose = 1;
137: else if ( strcmp( argv[argn], "-i" ) == 0 )
138: ; /* ignore */
139: else if ( strcmp( argv[argn], "-oi" ) == 0 )
140: ; /* ignore */
141: else if ( strcmp( argv[argn], "--" ) == 0 )
142: ; /* ignore */
143: else
144: usage();
145: ++argn;
146: }
147:
148: username = getlogin();
149: if ( username == (char*) 0 )
150: {
151: #ifdef DO_GETPWUID
152: struct passwd* pw = getpwuid( getuid() );
153: if ( pw == (struct passwd*) 0 )
154: {
155: (void) fprintf( stderr, "%s: can't determine username\n", argv0 );
156: exit( 1 );
157: }
158: username = pw->pw_name;
159: #else /* DO_GETPWUID */
160: (void) fprintf( stderr, "%s: can't determine username\n", argv0 );
161: exit( 1 );
162: #endif /* DO_GETPWUID */
163: }
164:
165: if ( gethostname( hostname, sizeof(hostname) - 1 ) < 0 )
166: show_error( "gethostname" );
167:
168: if ( fake_from == (char*) 0 )
169: (void) snprintf( from, sizeof(from), "%s@%s", username, hostname );
170: else
171: if ( strchr( fake_from, '@' ) == (char*) 0 )
172: (void) snprintf( from, sizeof(from), "%s@%s", fake_from, hostname );
173: else
174: (void) snprintf( from, sizeof(from), "%s", fake_from );
175:
176: /* Strip off any angle brackets in the from address. */
177: while ( from[0] == '<' )
178: (void) strcpy( from, &from[1] );
179: while ( from[strlen(from)-1] == '>' )
180: from[strlen(from)-1] = '\0';
181:
182: message = slurp_message();
183: #ifdef DO_RECEIVED
184: received = make_received( from, username, hostname );
185: #endif /* DO_RECEIVED */
186:
187: (void) signal( SIGALRM, sigcatch );
188:
189: (void) alarm( timeout );
190: sockfd1 = open_client_socket();
191:
192: sockfd2 = dup( sockfd1 );
193: sockrfp = fdopen( sockfd1, "r" );
194: sockwfp = fdopen( sockfd2, "w" );
195:
196: /* The full SMTP protocol is spelled out in RFC821, available at
197: ** http://www.faqs.org/rfcs/rfc821.html
198: ** The only non-obvious wrinkles:
199: ** - The commands are terminated with CRLF, not newline.
200: ** - Newlines in the data file get turned into CRLFs.
201: ** - Any data lines beginning with a period get an extra period prepended.
202: */
203:
204: status = read_response();
205: if ( status != 220 )
206: {
207: (void) fprintf(
208: stderr, "%s: unexpected initial greeting %d\n", argv0, status );
209: exit( 1 );
210: }
211:
212: (void) snprintf( buf, sizeof(buf), "HELO %s", hostname );
213: send_command( buf );
214: status = read_response();
215: if ( status != 250 )
216: {
217: (void) fprintf(
218: stderr, "%s: unexpected response %d to HELO command\n",
219: argv0, status );
220: exit( 1 );
221: }
222:
223: (void) snprintf( buf, sizeof(buf), "MAIL FROM:<%s>", from );
224: send_command( buf );
225: status = read_response();
226: if ( status != 250 )
227: {
228: (void) fprintf(
229: stderr, "%s: unexpected response %d to MAIL FROM command\n",
230: argv0, status );
231: exit( 1 );
232: }
233:
234: got_a_recipient = 0;
235: for ( ; argn < argc; ++argn )
236: add_recipient( argv[argn], strlen( argv[argn] ) );
237: if ( parse_message )
238: parse_for_recipients( message );
239: if ( ! got_a_recipient )
240: {
241: (void) fprintf( stderr, "%s: no recipients found\n", argv0 );
242: exit( 1 );
243: }
244:
245: send_command( "DATA" );
246: status = read_response();
247: if ( status != 354 )
248: {
249: (void) fprintf(
250: stderr, "%s: unexpected response %d to DATA command\n",
251: argv0, status );
252: exit( 1 );
253: }
254:
255: #ifdef DO_RECEIVED
256: send_data( received );
257: #endif /* DO_RECEIVED */
258: send_data( message );
259: send_done();
260: status = read_response();
261: if ( status != 250 )
262: {
263: (void) fprintf(
264: stderr, "%s: unexpected response %d to DATA\n", argv0, status );
265: exit( 1 );
266: }
267:
268: send_command( "QUIT" );
269: status = read_response();
270: if ( status != 221 )
271: (void) fprintf(
272: stderr, "%s: unexpected response %d to QUIT command - ignored\n",
273: argv0, status );
274:
275: (void) close( sockfd1 );
276: (void) close( sockfd2 );
277:
278: exit( 0 );
279: }
280:
281:
282: static void
283: usage( void )
284: {
285: #ifdef DO_MINUS_SP
286: #ifdef DO_DNS
287: char* spflag = "[-s<server>] [-p<port>] ";
288: #else /* DO_DNS */
289: char* spflag = "[-s<server_ip>] [-p<port>] ";
290: #endif /* DO_DNS */
291: #else /* DO_MINUS_SP */
292: char* spflag = "";
293: #endif /* DO_MINUS_SP */
294: (void) fprintf( stderr, "usage: %s [-f<name>] [-t] %s[-T<timeout>] [-v] [address ...]\n", argv0, spflag );
295: exit( 1 );
296: }
297:
298:
299: static char*
300: slurp_message( void )
301: {
302: char* message;
303: int message_size, message_len;
304: int c;
305:
306: message_size = 5000;
307: message = (char*) malloc( message_size );
308: if ( message == (char*) 0 )
309: {
310: (void) fprintf( stderr, "%s: out of memory\n", argv0 );
311: exit( 1 );
312: }
313: message_len = 0;
314:
315: for (;;)
316: {
317: c = getchar();
318: if ( c == EOF )
319: break;
320: if ( message_len + 1 >= message_size )
321: {
322: message_size *= 2;
323: message = (char*) realloc( (void*) message, message_size );
324: if ( message == (char*) 0 )
325: {
326: (void) fprintf( stderr, "%s: out of memory\n", argv0 );
327: exit( 1 );
328: }
329: }
330: message[message_len++] = c;
331: }
332: message[message_len] = '\0';
333:
334: return message;
335: }
336:
337:
338: #ifdef DO_RECEIVED
339: static char*
340: make_received( char* from, char* username, char* hostname )
341: {
342: int received_size;
343: char* received;
344: time_t t;
345: struct tm* tmP;
346: char timestamp[100];
347:
348: t = time( (time_t*) 0 );
349: tmP = localtime( &t );
350: (void) strftime( timestamp, sizeof(timestamp), "%a, %d %b %Y %T %Z", tmP );
351: received_size =
352: 500 + strlen( from ) + strlen( hostname ) * 2 + strlen( VERSION ) +
353: strlen( timestamp ) + strlen( username );
354: received = (char*) malloc( received_size );
355: if ( received == (char*) 0 )
356: {
357: (void) fprintf( stderr, "%s: out of memory\n", argv0 );
358: exit( 1 );
359: }
360: (void) snprintf(
361: received, received_size,
362: "Received: (from %s)\n\tby %s (%s);\n\t%s\n\t(sender %s@%s)\n",
363: from, hostname, VERSION, timestamp, username, hostname );
364: return received;
365: }
366: #endif /* DO_RECEIVED */
367:
368:
369: static void
370: parse_for_recipients( char* message )
371: {
372: /* A little finite-state machine that parses the message, looking
373: ** for To:, Cc:, and Bcc: recipients.
374: */
375: int state;
376: #define ST_BOL 0
377: #define ST_OTHERHEAD 1
378: #define ST_T 2
379: #define ST_C 3
380: #define ST_B 4
381: #define ST_BC 5
382: #define ST_RECIPHEAD 6
383: #define ST_RECIPS 7
384: char* cp;
385: char* bcc;
386: char* recip;
387:
388: state = ST_BOL;
389: bcc = (char*) 0;
390: for ( cp = message; *cp != '\0'; ++cp )
391: {
392: switch ( state )
393: {
394: case ST_BOL:
395: switch ( *cp )
396: {
397: case '\n':
398: return;
399: case 'T':
400: case 't':
401: state = ST_T;
402: break;
403: case 'C':
404: case 'c':
405: state = ST_C;
406: break;
407: case 'B':
408: case 'b':
409: state = ST_B;
410: bcc = cp;
411: break;
412: default:
413: state = ST_OTHERHEAD;
414: break;
415: }
416: break;
417: case ST_OTHERHEAD:
418: switch ( *cp )
419: {
420: case '\n':
421: state = ST_BOL;
422: break;
423: }
424: break;
425: case ST_T:
426: switch ( *cp )
427: {
428: case '\n':
429: state = ST_BOL;
430: break;
431: case 'O':
432: case 'o':
433: state = ST_RECIPHEAD;
434: break;
435: default:
436: state = ST_OTHERHEAD;
437: break;
438: }
439: break;
440: case ST_C:
441: switch ( *cp )
442: {
443: case '\n':
444: state = ST_BOL;
445: break;
446: case 'C':
447: case 'c':
448: state = ST_RECIPHEAD;
449: break;
450: default:
451: state = ST_OTHERHEAD;
452: break;
453: }
454: break;
455: case ST_B:
456: switch ( *cp )
457: {
458: case '\n':
459: state = ST_BOL;
460: bcc = (char*) 0;
461: break;
462: case 'C':
463: case 'c':
464: state = ST_BC;
465: break;
466: default:
467: state = ST_OTHERHEAD;
468: bcc = (char*) 0;
469: break;
470: }
471: break;
472: case ST_BC:
473: switch ( *cp )
474: {
475: case '\n':
476: state = ST_BOL;
477: bcc = (char*) 0;
478: break;
479: case 'C':
480: case 'c':
481: state = ST_RECIPHEAD;
482: break;
483: default:
484: state = ST_OTHERHEAD;
485: bcc = (char*) 0;
486: break;
487: }
488: break;
489: case ST_RECIPHEAD:
490: switch ( *cp )
491: {
492: case '\n':
493: state = ST_BOL;
494: bcc = (char*) 0;
495: break;
496: case ':':
497: state = ST_RECIPS;
498: recip = cp + 1;
499: break;
500: default:
501: state = ST_OTHERHEAD;
502: bcc = (char*) 0;
503: break;
504: }
505: break;
506: case ST_RECIPS:
507: switch ( *cp )
508: {
509: case '\n':
510: add_recipient( recip, ( cp - recip ) );
511: state = ST_BOL;
512: if ( bcc != (char*) 0 )
513: {
514: /* Elide the Bcc: line, and reset cp. */
515: (void) strcpy( bcc, cp + 1 );
516: cp = bcc - 1;
517: bcc = (char*) 0;
518: }
519: break;
520: case ',':
521: add_recipient( recip, ( cp - recip ) );
522: recip = cp + 1;
523: break;
524: }
525: break;
526: }
527: }
528: }
529:
530:
531: static void
532: add_recipient( char* recipient, int len )
533: {
534: char buf[1000];
535: int status;
536:
537: /* Skip leading whitespace. */
538: while ( len > 0 && ( *recipient == ' ' || *recipient == '\t' ) )
539: {
540: ++recipient;
541: --len;
542: }
543:
544: /* Strip off any angle brackets. */
545: /*
546: while ( len > 0 && *recipient == '<' )
547: {
548: ++recipient;
549: --len;
550: }
551: while ( len > 0 && recipient[len-1] == '>' )
552: --len;
553:
554: (void) snprintf( buf, sizeof(buf), "RCPT TO:<%.*s>", len, recipient );
555: */
556: if (len > 0 && recipient[len-1] == '>' )
557: {
558: /* "<name@domain>" or: "Full Name <name@domain>" */
559: while (len > 0 && *recipient != '<' )
560: {
561: ++recipient;
562: --len;
563: }
564: (void) snprintf( buf, sizeof(buf), "RCPT TO:%.*s", len, recipient );
565: }
566: else
567: {
568: /* name@domain */
569: (void) snprintf( buf, sizeof(buf), "RCPT TO:<%.*s>", len, recipient );
570: }
571:
572: send_command( buf );
573: status = read_response();
574: if ( status != 250 && status != 251 )
575: {
576: (void) fprintf(
577: stderr, "%s: unexpected response %d to RCPT TO command\n",
578: argv0, status );
579: exit( 1 );
580: }
581: got_a_recipient = 1;
582: }
583:
584:
585: #if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED)
586: #define USE_IPV6
587: #endif
588:
589: static int
590: open_client_socket( void )
591: {
592: #ifdef USE_IPV6
593: struct sockaddr_in6 sa;
594: #else /* USE_IPV6 */
595: struct sockaddr_in sa;
596: #endif /* USE_IPV6 */
597: int sa_len, sock_family, sock_type, sock_protocol;
598: int sockfd;
599:
600: sock_type = SOCK_STREAM;
601: sock_protocol = 0;
602: sa_len = sizeof(sa);
603: (void) memset( (void*) &sa, 0, sa_len );
604:
605: #ifdef USE_IPV6
606:
607: {
608: #ifdef DO_MINUS_SP
609: struct sockaddr_in sa4;
610: struct addrinfo hints;
611: char portstr[10];
612: int gaierr;
613: struct addrinfo* ai;
614: struct addrinfo* ai2;
615: struct addrinfo* aiv4;
616: struct addrinfo* aiv6;
617: #endif /* DO_MINUS_SP */
618:
619: sock_family = PF_INET6;
620:
621: #ifdef DO_MINUS_SP
622: (void) memset( (void*) &sa4, 0, sizeof(sa4) );
623: if ( inet_pton( AF_INET, server, (void*) &sa4.sin_addr ) == 1 )
624: {
625: sock_family = PF_INET;
626: sa4.sin_port = htons( port );
627: sa_len = sizeof(sa4);
628: (void) memmove( &sa, &sa4, sa_len );
629: }
630: else if ( inet_pton( AF_INET6, server, (void*) &sa.sin6_addr ) != 1 )
631: {
632: #ifdef DO_DNS
633: (void) memset( &hints, 0, sizeof(hints) );
634: hints.ai_family = PF_UNSPEC;
635: hints.ai_socktype = SOCK_STREAM;
636: (void) snprintf( portstr, sizeof(portstr), "%d", port );
637: if ( (gaierr = getaddrinfo( server, portstr, &hints, &ai )) != 0 )
638: {
639: (void) fprintf(
640: stderr, "%s: getaddrinfo %s - %s\n", argv0, server,
641: gai_strerror( gaierr ) );
642: exit( 1 );
643: }
644:
645: /* Find the first IPv4 and IPv6 entries. */
646: aiv4 = (struct addrinfo*) 0;
647: aiv6 = (struct addrinfo*) 0;
648: for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
649: {
650: switch ( ai2->ai_family )
651: {
652: case PF_INET:
653: if ( aiv4 == (struct addrinfo*) 0 )
654: aiv4 = ai2;
655: break;
656: case PF_INET6:
657: if ( aiv6 == (struct addrinfo*) 0 )
658: aiv6 = ai2;
659: break;
660: }
661: }
662:
663: /* If there's an IPv4 address, use that, otherwise try IPv6. */
664: if ( aiv4 != (struct addrinfo*) 0 )
665: {
666: if ( sizeof(sa) < aiv4->ai_addrlen )
667: {
668: (void) fprintf(
669: stderr, "%s - sockaddr too small (%lu < %lu)\n",
670: server, (unsigned long) sizeof(sa),
671: (unsigned long) aiv4->ai_addrlen );
672: exit( 1 );
673: }
674: sock_family = aiv4->ai_family;
675: sock_type = aiv4->ai_socktype;
676: sock_protocol = aiv4->ai_protocol;
677: sa_len = aiv4->ai_addrlen;
678: (void) memmove( &sa, aiv4->ai_addr, sa_len );
679: goto ok;
680: }
681: if ( aiv6 != (struct addrinfo*) 0 )
682: {
683: if ( sizeof(sa) < aiv6->ai_addrlen )
684: {
685: (void) fprintf(
686: stderr, "%s - sockaddr too small (%lu < %lu)\n",
687: server, (unsigned long) sizeof(sa),
688: (unsigned long) aiv6->ai_addrlen );
689: exit( 1 );
690: }
691: sock_family = aiv6->ai_family;
692: sock_type = aiv6->ai_socktype;
693: sock_protocol = aiv6->ai_protocol;
694: sa_len = aiv6->ai_addrlen;
695: (void) memmove( &sa, aiv6->ai_addr, sa_len );
696: goto ok;
697: }
698:
699: (void) fprintf(
700: stderr, "%s: no valid address found for host %s\n", argv0, server );
701: exit( 1 );
702:
703: ok:
704: freeaddrinfo( ai );
705: #else /* DO_DNS */
706: (void) fprintf(
707: stderr, "%s: bad server IP address %s\n", argv0, server );
708: exit( 1 );
709: #endif /* DO_DNS */
710: }
711: #else /* DO_MINUS_SP */
712: sa.sin6_addr = in6addr_any;
713: sa.sin6_port = htons( SMTP_PORT );
714: #endif /* DO_MINUS_SP */
715:
716: sa.sin6_family = sock_family;
717:
718: }
719:
720: #else /* USE_IPV6 */
721:
722: {
723: #ifdef DO_MINUS_SP
724: struct hostent *he;
725: #else /* DO_MINUS_SP */
726: char local_addr[4] = { 127, 0, 0, 1 };
727: #endif /* DO_MINUS_SP */
728:
729: sock_family = PF_INET;
730:
731: #ifdef DO_MINUS_SP
732: sa.sin_addr.s_addr = inet_addr( server );
733: sa.sin_port = htons( port );
734: if ( (int32_t) sa.sin_addr.s_addr == -1 )
735: {
736: #ifdef DO_DNS
737: he = gethostbyname( server );
738: if ( he == (struct hostent*) 0 )
739: {
740: (void) fprintf(
741: stderr, "%s: server name lookup of '%s' failed - %s\n",
742: argv0, server, hstrerror( h_errno ) );
743: exit( 1 );
744: }
745: sock_family = he->h_addrtype;
746: (void) memmove( &sa.sin_addr, he->h_addr, he->h_length );
747: #else /* DO_DNS */
748: (void) fprintf(
749: stderr, "%s: bad server IP address %s\n", argv0, server );
750: exit( 1 );
751: #endif /* DO_DNS */
752: }
753: #else /* DO_MINUS_SP */
754: (void) memmove( &sa.sin_addr, local_addr, sizeof(local_addr) );
755: sa.sin_port = htons( SMTP_PORT );
756: #endif /* DO_MINUS_SP */
757:
758: sa.sin_family = sock_family;
759: }
760:
761: #endif /* USE_IPV6 */
762:
763: sockfd = socket( sock_family, sock_type, sock_protocol );
764: if ( sockfd < 0 )
765: show_error( "socket" );
766:
767: if ( connect( sockfd, (struct sockaddr*) &sa, sa_len ) < 0 )
768: show_error( "connect" );
769:
770: return sockfd;
771: }
772:
773:
774: static int
775: read_response( void )
776: {
777: char buf[10000];
778: char* cp;
779: int status;
780:
781: for (;;)
782: {
783: (void) alarm( timeout );
784: if ( fgets( buf, sizeof(buf), sockrfp ) == (char*) 0 )
785: {
786: (void) fprintf( stderr, "%s: unexpected EOF\n", argv0 );
787: exit( 1 );
788: }
789: if ( verbose )
790: (void) fprintf( stderr, "<<<< %s", buf );
791: for ( status = 0, cp = buf; *cp >= '0' && *cp <= '9'; ++cp )
792: status = 10 * status + ( *cp - '0' );
793: if ( *cp == ' ' )
794: break;
795: if ( *cp != '-' )
796: {
797: (void) fprintf(
798: stderr, "%s: bogus reply syntax - '%s'\n", argv0, buf );
799: exit( 1 );
800: }
801: }
802: return status;
803: }
804:
805:
806: static void
807: send_command( char* command )
808: {
809: (void) alarm( timeout );
810: if ( verbose )
811: (void) fprintf( stderr, ">>>> %s\n", command );
812: (void) fprintf( sockwfp, "%s\r\n", command );
813: (void) fflush( sockwfp );
814: }
815:
816:
817: static void
818: send_data( char* data )
819: {
820: int bol;
821:
822: for ( bol = 1; *data != '\0'; ++data )
823: {
824: if ( bol && *data == '.' )
825: putc( '.', sockwfp );
826: bol = 0;
827: if ( *data == '\n' )
828: {
829: putc( '\r', sockwfp );
830: bol = 1;
831: }
832: putc( *data, sockwfp );
833: }
834: if ( ! bol )
835: (void) fputs( "\r\n", sockwfp );
836: }
837:
838:
839: static void
840: send_done( void )
841: {
842: (void) fputs( ".\r\n", sockwfp );
843: (void) fflush( sockwfp );
844: }
845:
846:
847: static void
848: sigcatch( int sig )
849: {
850: (void) fprintf( stderr, "%s: timed out\n", argv0 );
851: exit( 1 );
852: }
853:
854:
855: static void
856: show_error( char* cause )
857: {
858: char buf[5000];
859:
860: (void) snprintf( buf, sizeof(buf), "%s: %s", argv0, cause );
861: perror( buf );
862: exit( 1 );
863: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>