1 /*
2 * Copyright (c) 2014 Gilles Chehade <gilles@poolp.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "includes.h"
18
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22
23 #include <ctype.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31
32 #include <smtpd-api.h>
33
34 static char *config;
35 static int sock = -1;
36 static FILE *sockstream;
37 #define REPLYBUFFERSIZE 100000
38 static char repbuffer[REPLYBUFFERSIZE+1];
39
40 enum socketmap_reply{
41 SM_OK = 0,
42 SM_NOTFOUND,
43 SM_TEMP,
44 SM_TIMEOUT,
45 SM_PERM,
46 };
47
48 static int
table_socketmap_connect(const char * s)49 table_socketmap_connect(const char *s)
50 {
51 struct sockaddr_un sun;
52
53 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
54 log_warn("warn: socket");
55 goto err;
56 }
57
58 memset(&sun, 0, sizeof sun);
59 sun.sun_family = AF_UNIX;
60 if (strlcpy(sun.sun_path, s, sizeof(sun.sun_path)) >=
61 sizeof(sun.sun_path)) {
62 log_warnx("warn: socket path too long");
63 goto err;
64 }
65
66 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
67 log_warn("warn: connect");
68 goto err;
69 }
70
71 if ((sockstream = fdopen(sock, "w+")) == NULL) {
72 log_warn("warn: fdopen");
73 goto err;
74 }
75
76 return 1;
77
78 err:
79 if (sock == -1) {
80 close(sock);
81 sock = -1;
82 }
83 return 0;
84 }
85
86 static enum socketmap_reply
table_socketmap_query(const char * name,const char * key)87 table_socketmap_query(const char *name, const char *key)
88 {
89 char *buf = NULL;
90 size_t sz = 0;
91 ssize_t len;
92 int ret = SM_PERM;
93
94 memset(repbuffer, 0, sizeof repbuffer);
95 fprintf(sockstream, "%s %s\n", name, key);
96 fflush(sockstream);
97
98 if ((len = getline(&buf, &sz, sockstream)) == -1) {
99 log_warnx("warn: socketmap has lost its socket");
100 (void)strlcpy(repbuffer, "lost connection to socket", sizeof repbuffer);
101 ret = SM_PERM;
102 goto err;
103 }
104 if (buf[len - 1] == '\n')
105 buf[len - 1] = '\0';
106
107 if (strlcpy(repbuffer, buf, sizeof repbuffer) >= sizeof repbuffer) {
108 log_warnx("warn: socketmap reply too large (>%zu bytes)",
109 sizeof repbuffer);
110 (void)strlcpy(repbuffer, "socketmap reply too large", sizeof repbuffer);
111 ret = SM_PERM;
112 goto err;
113 }
114
115 if (strncasecmp(repbuffer, "OK ", 3) == 0) {
116 ret = SM_OK;
117 memmove(repbuffer, repbuffer+3, strlen(repbuffer)-2);
118 }
119 else if (strncasecmp(repbuffer, "NOTFOUND ", 9) == 0) {
120 ret = SM_NOTFOUND;
121 memmove(repbuffer, repbuffer+9, strlen(repbuffer)-8);
122 }
123 else if (strncasecmp(repbuffer, "TEMP ", 5) == 0) {
124 ret = SM_TEMP;
125 memmove(repbuffer, repbuffer+5, strlen(repbuffer)-4);
126 }
127 else if (strncasecmp(repbuffer, "TIMEOUT ", 8) == 0) {
128 ret = SM_TIMEOUT;
129 memmove(repbuffer, repbuffer+8, strlen(repbuffer)-7);
130 }
131 else if (strncasecmp(repbuffer, "PERM ", 5) == 0) {
132 ret = SM_PERM;
133 memmove(repbuffer, repbuffer+5, strlen(repbuffer)-4);
134 }
135 else {
136 ret = SM_PERM;
137 (void)strlcpy(repbuffer, "unrecognized socketmap reply", sizeof repbuffer);
138 }
139
140 err:
141 free(buf);
142 return ret;
143 }
144
145 static int
table_socketmap_update(void)146 table_socketmap_update(void)
147 {
148 return 1;
149 }
150
151 static int
table_socketmap_check(int service,struct dict * params,const char * key)152 table_socketmap_check(int service, struct dict *params, const char *key)
153 {
154 return -1;
155 }
156
157 static int
table_socketmap_lookup(int service,struct dict * params,const char * key,char * dst,size_t sz)158 table_socketmap_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz)
159 {
160 int r;
161 enum socketmap_reply rep;
162
163 rep = table_socketmap_query(table_api_get_name(), key);
164 if (rep == SM_NOTFOUND)
165 return 0;
166 if (rep != SM_OK) {
167 log_warnx("warn: %s", repbuffer);
168 return -1;
169 }
170 if (strlcpy(dst, repbuffer, sz) >= sz) {
171 log_warnx("warn: result too large");
172 return -1;
173 }
174
175 r = 1;
176 switch(service) {
177 case K_ALIAS:
178 case K_CREDENTIALS:
179 case K_USERINFO:
180 case K_DOMAIN:
181 case K_NETADDR:
182 case K_SOURCE:
183 case K_MAILADDR:
184 case K_ADDRNAME:
185 break;
186 default:
187 log_warnx("warn: unknown service %d", service);
188 r = -1;
189 }
190
191 return r;
192 }
193
194 static int
table_socketmap_fetch(int service,struct dict * params,char * key,size_t sz)195 table_socketmap_fetch(int service, struct dict *params, char *key, size_t sz)
196 {
197 return -1;
198 }
199
200 int
main(int argc,char ** argv)201 main(int argc, char **argv)
202 {
203 int ch;
204
205 log_init(1);
206 log_verbose(~0);
207
208 while ((ch = getopt(argc, argv, "")) != -1) {
209 switch (ch) {
210 default:
211 fatalx("bad option");
212 /* NOTREACHED */
213 }
214 }
215 argc -= optind;
216 argv += optind;
217
218 if (argc != 1)
219 fatalx("bogus argument(s)");
220
221
222 config = argv[0];
223
224 if (table_socketmap_connect(config) == 0)
225 fatalx("error connecting to %s", config);
226
227 table_api_on_update(table_socketmap_update);
228 table_api_on_check(table_socketmap_check);
229 table_api_on_lookup(table_socketmap_lookup);
230 table_api_on_fetch(table_socketmap_fetch);
231 table_api_dispatch();
232
233 return 0;
234 }
235