1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * mconnect.c - A program to test out SMTP connections.
30  * Usage: mconnect [host]
31  *  ... SMTP dialog
32  *  ^C or ^D or QUIT
33  */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <signal.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <strings.h>
41 #include <sgtty.h>
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <sys/ioctl.h>
45 #include <unistd.h>
46 #include <netinet/in.h>
47 #include <netdb.h>
48 #include <arpa/nameser.h>
49 #include <arpa/inet.h>
50 #include <errno.h>
51 
52 union bigsockaddr
53 {
54 	struct sockaddr		sa;	/* general version */
55 	struct sockaddr_in	sin;	/* INET family */
56 	struct sockaddr_in6	sin6;	/* INET/IPv6 */
57 };
58 
59 static struct sgttyb TtyBuf;
60 static int raw = 0;
61 
62 /* ARGSUSED */
63 static void
64 finis(sig)
65 	int sig;
66 {
67 	if (raw)
68 		(void) ioctl(0, TIOCSETP, &TtyBuf);
69 	exit(0);
70 }
71 
72 int
73 main(argc, argv)
74 	int argc;
75 	char **argv;
76 {
77 	union bigsockaddr SendmailAddress;
78 	register int s;
79 	char *host = NULL;
80 	int pid;
81 	int on = 1;
82 	struct servent *sp;
83 	char buf[1000];
84 	register FILE *f;
85 	register struct hostent *hp;
86 	in_port_t port = 0;
87 	int err;
88 	char buf6[INET6_ADDRSTRLEN];
89 	int addr_num = 0;
90 	int addrlen;
91 
92 	(void) ioctl(0, TIOCGETP, &TtyBuf);
93 	(void) signal(SIGINT, finis);
94 
95 	while (--argc > 0)
96 	{
97 		register char *p;
98 
99 		p = *++argv;
100 		if (*p == '-')
101 		{
102 			switch (*++p)
103 			{
104 			    case 'h':		/* host */
105 				break;
106 
107 			    case 'p':		/* port */
108 				port = htons(atoi(*++argv));
109 				argc--;
110 				break;
111 
112 			    case 'r':		/* raw connection */
113 				raw = 1;
114 				break;
115 			}
116 		} else if (host == NULL)
117 			host = p;
118 	}
119 	if (host == NULL)
120 		host = "localhost";
121 
122 	bzero(&SendmailAddress, sizeof (SendmailAddress));
123 	hp = getipnodebyname(host, AF_INET6, AI_DEFAULT|AI_ALL, &err);
124 	if (hp == NULL)
125 	{
126 		(void) fprintf(stderr, "mconnect: unknown host %s\r\n", host);
127 		exit(0);
128 	}
129 
130 	if (port == 0) {
131 		sp = getservbyname("smtp", "tcp");
132 		if (sp != NULL)
133 			port = sp->s_port;
134 	}
135 
136 	for (;;) {
137 		bcopy(hp->h_addr_list[addr_num],
138 		    &SendmailAddress.sin6.sin6_addr, IN6ADDRSZ);
139 		if (IN6_IS_ADDR_V4MAPPED(&SendmailAddress.sin6.sin6_addr)) {
140 			SendmailAddress.sa.sa_family = AF_INET;
141 			SendmailAddress.sin.sin_port = port;
142 			bcopy(&hp->h_addr_list[addr_num][IN6ADDRSZ - INADDRSZ],
143 				&SendmailAddress.sin.sin_addr, INADDRSZ);
144 			addrlen = sizeof (struct sockaddr_in);
145 		} else {
146 			SendmailAddress.sa.sa_family = AF_INET6;
147 			SendmailAddress.sin6.sin6_port = port;
148 			addrlen = sizeof (struct sockaddr_in6);
149 		}
150 
151 		s = socket(SendmailAddress.sa.sa_family, SOCK_STREAM, 0);
152 		if (s < 0)
153 		{
154 			perror("socket");
155 			exit(-1);
156 		}
157 		(void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
158 		    sizeof (on));
159 		if (SendmailAddress.sa.sa_family == AF_INET)
160 			(void) printf("connecting to host %s (%s), port %d\r\n",
161 				host, inet_ntoa(SendmailAddress.sin.sin_addr),
162 				ntohs(SendmailAddress.sin.sin_port));
163 		else
164 			(void) printf("connecting to host %s (%s), port %d\r\n",
165 				host, inet_ntop(AF_INET6,
166 					SendmailAddress.sin6.sin6_addr.s6_addr,
167 					buf6, sizeof (buf6)),
168 				ntohs(SendmailAddress.sin6.sin6_port));
169 		if (connect(s, (struct sockaddr *)&SendmailAddress,
170 				addrlen) >= 0)
171 			break;
172 		if (hp->h_addr_list[++addr_num] != NULL) {
173 			(void) printf("connect failed (%s), next address ...\n",
174 				strerror(errno));
175 			bcopy(hp->h_addr_list[addr_num],
176 				&SendmailAddress.sin6.sin6_addr, IN6ADDRSZ);
177 			if (IN6_IS_ADDR_V4MAPPED(
178 			    &SendmailAddress.sin6.sin6_addr)) {
179 				SendmailAddress.sa.sa_family = AF_INET;
180 				bcopy(&hp->h_addr_list[addr_num]
181 				    [IN6ADDRSZ - INADDRSZ],
182 					&SendmailAddress.sin.sin_addr,
183 					INADDRSZ);
184 				addrlen = sizeof (struct sockaddr_in);
185 			} else {
186 				SendmailAddress.sa.sa_family = AF_INET6;
187 				addrlen = sizeof (struct sockaddr_in6);
188 			}
189 			continue;
190 		}
191 		perror("connect");
192 		exit(-1);
193 	}
194 
195 	if (raw) {
196 		TtyBuf.sg_flags &= ~CRMOD;
197 		(void) ioctl(0, TIOCSETP, &TtyBuf);
198 		TtyBuf.sg_flags |= CRMOD;
199 	}
200 
201 	/* good connection, fork both sides */
202 	(void) printf("connection open\n");
203 	pid = fork();
204 	if (pid < 0)
205 	{
206 		perror("fork");
207 		exit(-1);
208 	}
209 	if (pid == 0)
210 	{
211 		/* child -- standard input to sendmail */
212 		int c;
213 
214 		f = fdopen(s, "w");
215 		while ((c = fgetc(stdin)) >= 0)
216 		{
217 			if (!raw && c == '\n')
218 				(void) fputc('\r', f);
219 			(void) fputc(c, f);
220 			if (c == '\n')
221 				(void) fflush(f);
222 		}
223 		(void) shutdown(s, 1);
224 		(void) sleep(10);
225 	}
226 	else
227 	{
228 		/* parent -- sendmail to standard output */
229 		f = fdopen(s, "r");
230 		while (fgets(buf, sizeof (buf), f) != NULL)
231 		{
232 			(void) fputs(buf, stdout);
233 			(void) fflush(stdout);
234 		}
235 		(void) kill(pid, SIGTERM);
236 	}
237 	if (raw)
238 		(void) ioctl(0, TIOCSETP, &TtyBuf);
239 	return (0);
240 }
241