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