1 /*
2  * Copyright (c) 2006-2017 Alon Bar-Lev <alon.barlev@gmail.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *     o Redistributions of source code must retain the above copyright notice,
9  *       this list of conditions and the following disclaimer.
10  *     o Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  *     o Neither the name of the <ORGANIZATION> nor the names of its
14  *       contributors may be used to endorse or promote products derived from
15  *       this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #include <errno.h>
35 #include <getopt.h>
36 #include <poll.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/ioctl.h>
42 #include <sys/socket.h>
43 #include <sys/un.h>
44 #include <unistd.h>
45 
46 static volatile int s_stop = 0;
47 
sigterm(int signo)48 static RETSIGTYPE sigterm(int signo) {
49 	(void)signo;
50 	s_stop = 1;
51 #if RETSIGTYPE != void
52 	return 0
53 #endif
54 }
55 
56 static void usage(char *name) {
57 	printf (
58 		(
59 "%s %s\n"
60 "\n"
61 "Copyright (c) 2006-2017 Alon Bar-Lev <alon.barlev@gmail.com>\n"
62 "This program comes with ABSOLUTELY NO WARRANTY.\n"
63 "This is free software, and you are welcome to redistribute it\n"
64 "under certain conditions. See the file COPYING for details.\n"
65 "\n"
66 "Syntax: %s [options]\n"
67 "Smartcard daemon for GnuPG\n"
68 "\n"
69 "Options:\n"
70 " \n"
71 "     --multi-server        run in multi server mode (foreground)\n"
72 "     --homedir             specify home directory\n"
73 "     --socket=FILE         use this socket\n"
74 " -v, --verbose             verbose\n"
75 "     --log-file            use a log file for the server\n"
76 "     --help                print this information\n"
77 		),
78 		PACKAGE,
79 		PACKAGE_VERSION,
80 		name
81 	);
82 }
83 
84 int main(int argc, char *argv[]) {
85 	struct sockaddr_un addr;
86 	int fd = -1;
87 	int ret = 1;
88 	long on = 1;
89 	char *socket_name = CONFIG_PROXY_SOCKET;
90 
91 	typedef struct fds_s {
92 		int fd;
93 		char *name;
94 		int peer;
95 		char buffer[1024];
96 		size_t buffer_n;
97 	} fds_t;
98 
99 	fds_t fds[] = {
100 		{ -1, "outgoing.out", 2, {0}, 0},
101 		{ 0, "incoming.in", 0, {0}, 0},
102 		{ 1, "incoming.out", 1, {0}, 0},
103 	};
104 	int fds_n = sizeof(fds) / sizeof(fds[0]);
105 	int disconnect = 0;
106 
107 	enum {
108 		OPT_MUTLI_SERVER,
109 		OPT_HOMEDIR,
110 		OPT_SOCKET,
111 		OPT_VERBOSE,
112 		OPT_LOG_FILE,
113 		OPT_VERSION,
114 		OPT_HELP
115 	};
116 
117 	static struct option long_options[] = {
118 		{ "multi-server", no_argument, NULL, OPT_MUTLI_SERVER },
119 		{ "homedir", required_argument, NULL, OPT_HOMEDIR },
120 		{ "socket", required_argument, NULL, OPT_SOCKET },
121 		{ "verbose", no_argument, NULL, OPT_VERBOSE },
122 		{ "log-file", required_argument, NULL, OPT_LOG_FILE },
123 		{ "version", no_argument, NULL, OPT_VERSION },
124 		{ "help", no_argument, NULL, OPT_HELP },
125 		{ NULL, 0, NULL, 0 }
126 	};
127 	int opt;
128 
129 	while ((opt = getopt_long (argc, argv, "v", long_options, NULL)) != -1) {
130 		switch (opt) {
131 			case OPT_MUTLI_SERVER:
132 			break;
133 			case OPT_HOMEDIR:
134 			break;
135 			case OPT_SOCKET:
136 				socket_name = optarg;
137 			break;
138 			case OPT_VERBOSE:
139 			case 'v':
140 			break;
141 			case OPT_LOG_FILE:
142 			break;
143 			case OPT_VERSION:
144 				printf (
145 					"%s %s\n"
146 					"\n"
147 					"Copyright (c) 2006-2017 Alon Bar-Lev <alon.barlev@gmail.com>\n"
148 					"\n"
149 					"This is free software; see the source for copying conditions.\n"
150 					"There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
151 					PACKAGE,
152 					PACKAGE_VERSION
153 				);
154 				exit (0);
155 			break;
156 			case OPT_HELP:
157 				usage(argv[0]);
158 				exit(0);
159 			break;
160 			default:
161 				fprintf(stderr, "invalid usage\n");
162 				exit(1);
163 			break;
164 		}
165 	}
166 
167 	signal(SIGPIPE, SIG_IGN);
168 	signal(SIGTERM, sigterm);
169 	signal(SIGINT, sigterm);
170 
171 	memset(&addr, 0, sizeof(addr));
172 	addr.sun_family = AF_UNIX;
173 
174 	if (strlen (socket_name) + 1 >= sizeof (addr.sun_path)) {
175 		fprintf(stderr, "Socket '%s' too long, expected %ld\n", socket_name, (long)sizeof (addr.sun_path));
176 		goto cleanup;
177 	}
178 	strcpy(addr.sun_path, socket_name);
179 
180 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
181 		perror("socket");
182 		goto cleanup;
183 	}
184 
185 	if (ioctl(fd, FIONBIO, &on) == -1) {
186 		perror("ioctl sock");
187 		goto cleanup;
188 	}
189 
190 	if (connect(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) {
191 		fprintf(stderr, "Cannot connect '%s': %s\n", socket_name, strerror(errno));
192 		goto cleanup;
193 	}
194 
195 	fds[0].fd = fd;
196 	while (!s_stop && !disconnect) {
197 		struct pollfd pollfds[fds_n];
198 		int i;
199 
200 		memset(&pollfds, 0, sizeof(pollfds));
201 
202 		for (i = 0; i < fds_n; i++) {
203 			fds_t *peer = &fds[fds[i].peer];
204 			pollfds[i].fd = fds[i].fd;
205 			if (peer->buffer_n < sizeof(peer->buffer)) {
206 				pollfds[i].events |= POLLIN;
207 			}
208 			if (fds[i].buffer_n > 0) {
209 				pollfds[i].events |= POLLOUT;
210 			}
211 		}
212 
213 		if (poll(pollfds, sizeof(pollfds) / sizeof(struct pollfd), -1) == -1) {
214 			if (errno != EINTR && errno != EAGAIN) {
215 				perror("poll");
216 				goto cleanup;
217 			}
218 			continue;
219 		}
220 
221 		for (i = 0; i < fds_n && !disconnect; i++) {
222 			if ((pollfds[i].revents & POLLHUP) != 0) {
223 				disconnect = 1;
224 			}
225 			if ((pollfds[i].revents & POLLERR) != 0) {
226 				fprintf(stderr, "error %s\n", fds[i].name);
227 				goto cleanup;
228 			}
229 		}
230 
231 		for (i = 0; i < fds_n && !disconnect; i++) {
232 			if ((pollfds[i].revents & POLLIN) != 0) {
233 				fds_t *peer = &fds[fds[i].peer];
234 				int n;
235 				if ((n = read(fds[i].fd, peer->buffer + peer->buffer_n, sizeof(peer->buffer) - peer->buffer_n)) == -1) {
236 					fprintf(stderr, "error %s read\n", fds[i].name);
237 					goto cleanup;
238 				}
239 				if (n == 0) {
240 					disconnect = 1;
241 				}
242 				peer->buffer_n += n;
243 			}
244 
245 			if ((pollfds[i].revents & POLLOUT) != 0) {
246 				int n;
247 				if ((n = write(fds[i].fd, fds[i].buffer, fds[i].buffer_n)) == -1) {
248 					fprintf(stderr, "error %s write\n", fds[i].name);
249 					goto cleanup;
250 				}
251 				fds[i].buffer_n -= n;
252 				memmove(fds[i].buffer, fds[i].buffer + n, fds[i].buffer_n);
253 			}
254 		}
255 	}
256 
257 	ret = 0;
258 
259 cleanup:
260 	if (fd != -1) {
261 		close(fd);
262 	}
263 
264 	return ret;
265 }
266