| 1 | /* | 
|---|
| 2 | * This Source Code Form is subject to the terms of the Mozilla Public | 
|---|
| 3 | * License, v. 2.0.  If a copy of the MPL was not distributed with this | 
|---|
| 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | 
|---|
| 5 | * | 
|---|
| 6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. | 
|---|
| 7 | */ | 
|---|
| 8 |  | 
|---|
| 9 | #include "monetdb_config.h" | 
|---|
| 10 |  | 
|---|
| 11 | #include <string.h>  /* strerror, strchr, strcmp */ | 
|---|
| 12 | #include <sys/types.h> | 
|---|
| 13 | #include <sys/socket.h> | 
|---|
| 14 | #include <sys/un.h> | 
|---|
| 15 | #include <netdb.h> | 
|---|
| 16 | #include <netinet/in.h> | 
|---|
| 17 | #ifdef HAVE_POLL_H | 
|---|
| 18 | #include <poll.h> | 
|---|
| 19 | #endif | 
|---|
| 20 | #ifdef HAVE_SYS_UIO_H | 
|---|
| 21 | # include <sys/uio.h> | 
|---|
| 22 | #endif | 
|---|
| 23 | #include <fcntl.h> | 
|---|
| 24 |  | 
|---|
| 25 | #include "msabaoth.h" | 
|---|
| 26 | #include "mcrypt.h" | 
|---|
| 27 | #include "stream.h" | 
|---|
| 28 | #include "stream_socket.h" | 
|---|
| 29 | #include "utils/utils.h" /* freeConfFile */ | 
|---|
| 30 | #include "utils/properties.h" /* readProps */ | 
|---|
| 31 |  | 
|---|
| 32 | #include "merovingian.h" | 
|---|
| 33 | #include "forkmserver.h" | 
|---|
| 34 | #include "proxy.h" | 
|---|
| 35 | #include "multiplex-funnel.h" | 
|---|
| 36 | #include "controlrunner.h" | 
|---|
| 37 | #include "client.h" | 
|---|
| 38 | #include "handlers.h" | 
|---|
| 39 |  | 
|---|
| 40 | #if !defined(HAVE_ACCEPT4) || !defined(SOCK_CLOEXEC) | 
|---|
| 41 | #define accept4(sockfd, addr, addrlen, flags)	accept(sockfd, addr, addrlen) | 
|---|
| 42 | #endif | 
|---|
| 43 |  | 
|---|
| 44 | struct threads { | 
|---|
| 45 | struct threads *next; | 
|---|
| 46 | pthread_t tid; | 
|---|
| 47 | volatile char dead; | 
|---|
| 48 | }; | 
|---|
| 49 | struct clientdata { | 
|---|
| 50 | int sock; | 
|---|
| 51 | bool isusock; | 
|---|
| 52 | struct threads *self; | 
|---|
| 53 | char challenge[32]; | 
|---|
| 54 | }; | 
|---|
| 55 |  | 
|---|
| 56 | static void * | 
|---|
| 57 | handleClient(void *data) | 
|---|
| 58 |  | 
|---|
| 59 | { | 
|---|
| 60 | stream *fdin, *fout; | 
|---|
| 61 | char buf[8096]; | 
|---|
| 62 | char chal[32]; | 
|---|
| 63 | char *user = NULL, *algo = NULL, *passwd = NULL, *lang = NULL; | 
|---|
| 64 | char *database = NULL, *s; | 
|---|
| 65 | char dbmod[64]; | 
|---|
| 66 | char host[512]; | 
|---|
| 67 | char port[16]; | 
|---|
| 68 | sabdb *top = NULL; | 
|---|
| 69 | sabdb *stat = NULL; | 
|---|
| 70 | struct sockaddr saddr; | 
|---|
| 71 | socklen_t saddrlen = 0; | 
|---|
| 72 | err e; | 
|---|
| 73 | confkeyval *ckv, *kv; | 
|---|
| 74 | char mydoproxy; | 
|---|
| 75 | sabdb redirs[24];  /* do we need more? */ | 
|---|
| 76 | int r = 0; | 
|---|
| 77 | int sock; | 
|---|
| 78 | bool isusock; | 
|---|
| 79 | struct threads *self; | 
|---|
| 80 |  | 
|---|
| 81 | sock = ((struct clientdata *) data)->sock; | 
|---|
| 82 | isusock = ((struct clientdata *) data)->isusock; | 
|---|
| 83 | self = ((struct clientdata *) data)->self; | 
|---|
| 84 | memcpy(chal, ((struct clientdata *) data)->challenge, sizeof(chal)); | 
|---|
| 85 | free(data); | 
|---|
| 86 | fdin = socket_rstream(sock, "merovingian<-client (read)"); | 
|---|
| 87 | if (fdin == 0) { | 
|---|
| 88 | self->dead = 1; | 
|---|
| 89 | return(newErr( "merovingian-client inputstream problems")); | 
|---|
| 90 | } | 
|---|
| 91 | fdin = block_stream(fdin); | 
|---|
| 92 |  | 
|---|
| 93 | fout = socket_wstream(sock, "merovingian->client (write)"); | 
|---|
| 94 | if (fout == 0) { | 
|---|
| 95 | close_stream(fdin); | 
|---|
| 96 | self->dead = 1; | 
|---|
| 97 | return(newErr( "merovingian-client outputstream problems")); | 
|---|
| 98 | } | 
|---|
| 99 | fout = block_stream(fout); | 
|---|
| 100 |  | 
|---|
| 101 | if (isusock) { | 
|---|
| 102 | snprintf(host, sizeof(host), "(local)"); | 
|---|
| 103 | } else if (getpeername(sock, &saddr, &saddrlen) == -1) { | 
|---|
| 104 | Mfprintf(stderr, "couldn't get peername of client: %s\n", strerror(errno)); | 
|---|
| 105 | snprintf(host, sizeof(host), "(unknown)"); | 
|---|
| 106 | } else { | 
|---|
| 107 | char ghost[512]; | 
|---|
| 108 | if (getnameinfo(&saddr, saddrlen, ghost, sizeof(ghost), port, sizeof(port), | 
|---|
| 109 | NI_NUMERICSERV | NI_NUMERICHOST) == 0) { | 
|---|
| 110 | snprintf(host, sizeof(host), "%s:%s", ghost, port); | 
|---|
| 111 | } else { | 
|---|
| 112 | snprintf(host, sizeof(host), "(unknown):%s", port); | 
|---|
| 113 | } | 
|---|
| 114 | } | 
|---|
| 115 |  | 
|---|
| 116 | /* note: since Jan2012 we speak proto 9 for control connections */ | 
|---|
| 117 | mnstr_printf(fout, "%s:merovingian:9:%s:%s:%s:", | 
|---|
| 118 | chal, | 
|---|
| 119 | mcrypt_getHashAlgorithms(), | 
|---|
| 120 | #ifdef WORDS_BIGENDIAN | 
|---|
| 121 | "BIG", | 
|---|
| 122 | #else | 
|---|
| 123 | "LIT", | 
|---|
| 124 | #endif | 
|---|
| 125 | MONETDB5_PASSWDHASH | 
|---|
| 126 | ); | 
|---|
| 127 | mnstr_flush(fout); | 
|---|
| 128 |  | 
|---|
| 129 | /* get response */ | 
|---|
| 130 | buf[0] = '\0'; | 
|---|
| 131 | if (mnstr_read_block(fdin, buf, sizeof(buf) - 1, 1) < 0) { | 
|---|
| 132 | /* we didn't get a terminated block :/ */ | 
|---|
| 133 | e = newErr( "client %s sent challenge in incomplete block: %s", | 
|---|
| 134 | host, buf); | 
|---|
| 135 | mnstr_printf(fout, "!monetdbd: client sent something this " | 
|---|
| 136 | "server could not understand, sorry\n"); | 
|---|
| 137 | mnstr_flush(fout); | 
|---|
| 138 | close_stream(fout); | 
|---|
| 139 | close_stream(fdin); | 
|---|
| 140 | self->dead = 1; | 
|---|
| 141 | return(e); | 
|---|
| 142 | } | 
|---|
| 143 | buf[sizeof(buf) - 1] = '\0'; | 
|---|
| 144 |  | 
|---|
| 145 | /* decode BIG/LIT:user:{cypher}passwordchal:lang:database: line */ | 
|---|
| 146 |  | 
|---|
| 147 | user = buf; | 
|---|
| 148 | /* byte order */ | 
|---|
| 149 | s = strchr(user, ':'); | 
|---|
| 150 | if (s) { | 
|---|
| 151 | *s = 0; | 
|---|
| 152 | /* we don't use this in merovingian */ | 
|---|
| 153 | /* mnstr_set_byteorder(fin->s, strcmp(user, "BIG") == 0); */ | 
|---|
| 154 | user = s + 1; | 
|---|
| 155 | } else { | 
|---|
| 156 | e = newErr( "client %s challenge error: %s", host, buf); | 
|---|
| 157 | mnstr_printf(fout, "!monetdbd: incomplete challenge '%s'\n", buf); | 
|---|
| 158 | mnstr_flush(fout); | 
|---|
| 159 | close_stream(fout); | 
|---|
| 160 | close_stream(fdin); | 
|---|
| 161 | self->dead = 1; | 
|---|
| 162 | return(e); | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | /* passwd */ | 
|---|
| 166 | s = strchr(user, ':'); | 
|---|
| 167 | if (s) { | 
|---|
| 168 | *s = 0; | 
|---|
| 169 | passwd = s + 1; | 
|---|
| 170 | /* decode algorithm, i.e. {plain}mypasswordchallenge */ | 
|---|
| 171 | if (*passwd != '{') { | 
|---|
| 172 | e = newErr( "client %s challenge error: %s", host, buf); | 
|---|
| 173 | mnstr_printf(fout, "!monetdbd: invalid password entry\n"); | 
|---|
| 174 | mnstr_flush(fout); | 
|---|
| 175 | close_stream(fout); | 
|---|
| 176 | close_stream(fdin); | 
|---|
| 177 | self->dead = 1; | 
|---|
| 178 | return(e); | 
|---|
| 179 | } | 
|---|
| 180 | algo = passwd + 1; | 
|---|
| 181 | s = strchr(algo, '}'); | 
|---|
| 182 | if (!s) { | 
|---|
| 183 | e = newErr( "client %s challenge error: %s", host, buf); | 
|---|
| 184 | mnstr_printf(fout, "!monetdbd: invalid password entry\n"); | 
|---|
| 185 | mnstr_flush(fout); | 
|---|
| 186 | close_stream(fout); | 
|---|
| 187 | close_stream(fdin); | 
|---|
| 188 | self->dead = 1; | 
|---|
| 189 | return(e); | 
|---|
| 190 | } | 
|---|
| 191 | *s = 0; | 
|---|
| 192 | passwd = s + 1; | 
|---|
| 193 | } else { | 
|---|
| 194 | e = newErr( "client %s challenge error: %s", host, buf); | 
|---|
| 195 | mnstr_printf(fout, "!monetdbd: incomplete challenge, missing password after '%s'\n", user); | 
|---|
| 196 | mnstr_flush(fout); | 
|---|
| 197 | close_stream(fout); | 
|---|
| 198 | close_stream(fdin); | 
|---|
| 199 | self->dead = 1; | 
|---|
| 200 | return(e); | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | /* lang */ | 
|---|
| 204 | s = strchr(passwd, ':'); | 
|---|
| 205 | if (s) { | 
|---|
| 206 | *s = 0; | 
|---|
| 207 | lang = s + 1; | 
|---|
| 208 | } else { | 
|---|
| 209 | e = newErr( "client %s challenge error: %s", host, buf); | 
|---|
| 210 | mnstr_printf(fout, "!monetdbd: incomplete challenge, missing language after '%s'\n", passwd); | 
|---|
| 211 | mnstr_flush(fout); | 
|---|
| 212 | close_stream(fout); | 
|---|
| 213 | close_stream(fdin); | 
|---|
| 214 | self->dead = 1; | 
|---|
| 215 | return(e); | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | /* database */ | 
|---|
| 219 | s = strchr(lang, ':'); | 
|---|
| 220 | if (s) { | 
|---|
| 221 | *s = 0; | 
|---|
| 222 | database = s + 1; | 
|---|
| 223 | /* since we don't know where the string ends, we need to look | 
|---|
| 224 | * for another : */ | 
|---|
| 225 | s = strchr(database, ':'); | 
|---|
| 226 | if (s == NULL) { | 
|---|
| 227 | e = newErr( "client %s challenge error: %s", host, buf); | 
|---|
| 228 | mnstr_printf(fout, "!monetdbd: incomplete challenge, missing trailing colon\n"); | 
|---|
| 229 | mnstr_flush(fout); | 
|---|
| 230 | close_stream(fout); | 
|---|
| 231 | close_stream(fdin); | 
|---|
| 232 | self->dead = 1; | 
|---|
| 233 | return(e); | 
|---|
| 234 | } else { | 
|---|
| 235 | *s = '\0'; | 
|---|
| 236 | } | 
|---|
| 237 | } | 
|---|
| 238 |  | 
|---|
| 239 | if (database == NULL || *database == '\0') { | 
|---|
| 240 | /* we need to have a database, if we haven't gotten one, | 
|---|
| 241 | * complain */ | 
|---|
| 242 | mnstr_printf(fout, "!monetdbd: please specify a database\n"); | 
|---|
| 243 | mnstr_flush(fout); | 
|---|
| 244 | close_stream(fout); | 
|---|
| 245 | close_stream(fdin); | 
|---|
| 246 | self->dead = 1; | 
|---|
| 247 | return(newErr( "client %s specified no database", host)); | 
|---|
| 248 | } | 
|---|
| 249 |  | 
|---|
| 250 | if (strcmp(lang, "control") == 0) { | 
|---|
| 251 | /* handle control client */ | 
|---|
| 252 | if (control_authorise(host, chal, algo, passwd, fout)) | 
|---|
| 253 | control_handleclient(host, sock, fdin, fout); | 
|---|
| 254 | close_stream(fout); | 
|---|
| 255 | close_stream(fdin); | 
|---|
| 256 | self->dead = 1; | 
|---|
| 257 | return(NO_ERR); | 
|---|
| 258 | } | 
|---|
| 259 |  | 
|---|
| 260 | if (strcmp(lang, "resolve") == 0) { | 
|---|
| 261 | /* ensure the pattern ends with '/\*' such that we force a | 
|---|
| 262 | * remote entry, including those for local databases, this | 
|---|
| 263 | * way we will get a redirect back to merovingian for such | 
|---|
| 264 | * database if it is proxied and hence not remotely | 
|---|
| 265 | * available */ | 
|---|
| 266 | size_t len = strlen(database); | 
|---|
| 267 | if (len > 2 && | 
|---|
| 268 | database[len - 2] != '/' && | 
|---|
| 269 | database[len - 1] != '*') | 
|---|
| 270 | { | 
|---|
| 271 | snprintf(dbmod, sizeof(dbmod), "%s/*", database); | 
|---|
| 272 | database = dbmod; | 
|---|
| 273 | } | 
|---|
| 274 | } | 
|---|
| 275 |  | 
|---|
| 276 | if ((e = forkMserver(database, &top, false)) != NO_ERR) { | 
|---|
| 277 | if (top == NULL) { | 
|---|
| 278 | mnstr_printf(fout, "!monetdbd: no such database '%s', please create it first\n", database); | 
|---|
| 279 | } else { | 
|---|
| 280 | mnstr_printf(fout, "!monetdbd: internal error while starting mserver '%s'%s\n", e, strstr(e, "logfile")? "": ", please refer to the logs"); | 
|---|
| 281 | Mfprintf(_mero_ctlerr, "!monetdbd: an internal error has occurred '%s'\n",e); | 
|---|
| 282 | } | 
|---|
| 283 | mnstr_flush(fout); | 
|---|
| 284 | close_stream(fout); | 
|---|
| 285 | close_stream(fdin); | 
|---|
| 286 | self->dead = 1; | 
|---|
| 287 | return(e); | 
|---|
| 288 | } | 
|---|
| 289 | stat = top; | 
|---|
| 290 |  | 
|---|
| 291 | /* a multiplex-funnel is a database which has no connections, but a | 
|---|
| 292 | * scenario "mfunnel" */ | 
|---|
| 293 | if ((top->conns == NULL || top->conns->val == NULL) && | 
|---|
| 294 | top->scens != NULL && strcmp(top->scens->val, "mfunnel") == 0) | 
|---|
| 295 | { | 
|---|
| 296 | multiplexAddClient(top->dbname, sock, fout, fdin, host); | 
|---|
| 297 | msab_freeStatus(&top); | 
|---|
| 298 | self->dead = 1; | 
|---|
| 299 | return(NO_ERR); | 
|---|
| 300 | } | 
|---|
| 301 |  | 
|---|
| 302 | /* collect possible redirects */ | 
|---|
| 303 | for (stat = top; stat != NULL; stat = stat->next) { | 
|---|
| 304 | if (stat->conns == NULL || stat->conns->val == NULL) { | 
|---|
| 305 | Mfprintf(stdout, "dropping database without available " | 
|---|
| 306 | "connections: '%s'\n", stat->dbname); | 
|---|
| 307 | } else if (r == 24) { | 
|---|
| 308 | Mfprintf(stdout, "dropping database connection because of " | 
|---|
| 309 | "too many already: %s\n", stat->conns->val); | 
|---|
| 310 | } else { | 
|---|
| 311 | redirs[r++] = *stat; | 
|---|
| 312 | } | 
|---|
| 313 | } | 
|---|
| 314 |  | 
|---|
| 315 | /* if we can't redirect, our mission ends here */ | 
|---|
| 316 | if (r == 0) { | 
|---|
| 317 | if (top->locked) { | 
|---|
| 318 | e = newErr( "database '%s' is under maintenance", top->dbname); | 
|---|
| 319 | } else { | 
|---|
| 320 | e = newErr( "there are no available connections for '%s'", database); | 
|---|
| 321 | } | 
|---|
| 322 | mnstr_printf(fout, "!monetdbd: %s\n", e); | 
|---|
| 323 | mnstr_flush(fout); | 
|---|
| 324 | close_stream(fout); | 
|---|
| 325 | close_stream(fdin); | 
|---|
| 326 | msab_freeStatus(&top); | 
|---|
| 327 | self->dead = 1; | 
|---|
| 328 | return(e); | 
|---|
| 329 | } | 
|---|
| 330 |  | 
|---|
| 331 | /* need to send a response, either we are going to proxy, or we send | 
|---|
| 332 | * a redirect, if we have multiple options, a redirect is our only | 
|---|
| 333 | * option, but if the redir is a single remote we need to stick to | 
|---|
| 334 | * our default, there is a special case when the client indicates it | 
|---|
| 335 | * is only resolving a pattern, in which we always need to send | 
|---|
| 336 | * redirects, even if it's one */ | 
|---|
| 337 | mydoproxy = 0; | 
|---|
| 338 | if (r == 1 && strcmp(lang, "resolve") != 0) { | 
|---|
| 339 | if (redirs[0].dbname != redirs[0].path) { | 
|---|
| 340 | /* this is a real local database (not a remote) */ | 
|---|
| 341 | ckv = getDefaultProps(); | 
|---|
| 342 | readProps(ckv, redirs[0].path); | 
|---|
| 343 | kv = findConfKey(ckv, "forward"); | 
|---|
| 344 | } else { | 
|---|
| 345 | ckv = NULL; | 
|---|
| 346 | kv = NULL; | 
|---|
| 347 | } | 
|---|
| 348 | if (kv == NULL || kv->val == NULL) | 
|---|
| 349 | kv = findConfKey(_mero_props, "forward"); | 
|---|
| 350 | mydoproxy = strcmp(kv->val, "proxy") == 0; | 
|---|
| 351 | if (ckv != NULL) { | 
|---|
| 352 | freeConfFile(ckv); | 
|---|
| 353 | free(ckv); | 
|---|
| 354 | } | 
|---|
| 355 | } | 
|---|
| 356 |  | 
|---|
| 357 | if (mydoproxy == 0) { | 
|---|
| 358 | fprintf(stdout, "redirecting client %s for database '%s' to", | 
|---|
| 359 | host, database); | 
|---|
| 360 | /* client is in control, send all redirects */ | 
|---|
| 361 | while (--r >= 0) { | 
|---|
| 362 | fprintf(stdout, " %s%s", | 
|---|
| 363 | redirs[r].conns->val, redirs[r].dbname); | 
|---|
| 364 | mnstr_printf(fout, "^%s%s\n", | 
|---|
| 365 | redirs[r].conns->val, redirs[r].dbname); | 
|---|
| 366 | } | 
|---|
| 367 | /* flush redirect */ | 
|---|
| 368 | fprintf(stdout, "\n"); | 
|---|
| 369 | fflush(stdout); | 
|---|
| 370 | mnstr_flush(fout); | 
|---|
| 371 | } else { | 
|---|
| 372 | Mfprintf(stdout, "proxying client %s for database '%s' to " | 
|---|
| 373 | "%s?database=%s\n", | 
|---|
| 374 | host, database, redirs[0].conns->val, redirs[0].dbname); | 
|---|
| 375 | /* merovingian is in control, only consider the first redirect */ | 
|---|
| 376 | mnstr_printf(fout, "^mapi:merovingian://proxy?database=%s\n", | 
|---|
| 377 | redirs[0].dbname); | 
|---|
| 378 | /* flush redirect */ | 
|---|
| 379 | mnstr_flush(fout); | 
|---|
| 380 |  | 
|---|
| 381 | /* wait for input, or disconnect in a proxy runner */ | 
|---|
| 382 | if ((e = startProxy(sock, fdin, fout, | 
|---|
| 383 | redirs[0].conns->val, host)) != NO_ERR) | 
|---|
| 384 | { | 
|---|
| 385 | /* we need to let the client login in order not to violate | 
|---|
| 386 | * the protocol */ | 
|---|
| 387 | mnstr_printf(fout, "void:merovingian:9:%s:BIG:%s:", | 
|---|
| 388 | mcrypt_getHashAlgorithms(), MONETDB5_PASSWDHASH); | 
|---|
| 389 | mnstr_flush(fout); | 
|---|
| 390 | mnstr_read_block(fdin, buf, 8095, 1); /* eat away client response */ | 
|---|
| 391 | mnstr_printf(fout, "!monetdbd: an internal error has occurred '%s', refer to the logs for details, please try again later\n",e); | 
|---|
| 392 | mnstr_flush(fout); | 
|---|
| 393 | Mfprintf(_mero_ctlerr, "!monetdbd: an internal error has occurred '%s'\n",e); | 
|---|
| 394 | close_stream(fout); | 
|---|
| 395 | close_stream(fdin); | 
|---|
| 396 | Mfprintf(stdout, "starting a proxy failed: %s\n", e); | 
|---|
| 397 | msab_freeStatus(&top); | 
|---|
| 398 | self->dead = 1; | 
|---|
| 399 | return(e); | 
|---|
| 400 | } | 
|---|
| 401 | } | 
|---|
| 402 |  | 
|---|
| 403 | msab_freeStatus(&top); | 
|---|
| 404 | self->dead = 1; | 
|---|
| 405 | return(NO_ERR); | 
|---|
| 406 | } | 
|---|
| 407 |  | 
|---|
| 408 | char * | 
|---|
| 409 | acceptConnections(int sock, int usock) | 
|---|
| 410 | { | 
|---|
| 411 | char *msg; | 
|---|
| 412 | int retval; | 
|---|
| 413 | #ifdef HAVE_POLL | 
|---|
| 414 | struct pollfd pfd[2]; | 
|---|
| 415 | #else | 
|---|
| 416 | fd_set fds; | 
|---|
| 417 | struct timeval tv; | 
|---|
| 418 | #endif | 
|---|
| 419 | int msgsock; | 
|---|
| 420 | void *e; | 
|---|
| 421 | struct clientdata *data; | 
|---|
| 422 | struct threads *threads = NULL, **threadp, *p; | 
|---|
| 423 | int errnr;					/* saved errno */ | 
|---|
| 424 |  | 
|---|
| 425 | do { | 
|---|
| 426 | /* handle socket connections */ | 
|---|
| 427 | bool isusock = false; | 
|---|
| 428 |  | 
|---|
| 429 | #ifdef HAVE_POLL | 
|---|
| 430 | pfd[0] = (struct pollfd) {.fd = sock, .events = POLLIN}; | 
|---|
| 431 | pfd[1] = (struct pollfd) {.fd = usock, .events = POLLIN}; | 
|---|
| 432 |  | 
|---|
| 433 | /* Wait up to 5 seconds */ | 
|---|
| 434 | retval = poll(pfd, 2, 5000); | 
|---|
| 435 | #else | 
|---|
| 436 | FD_ZERO(&fds); | 
|---|
| 437 | FD_SET(sock, &fds); | 
|---|
| 438 | FD_SET(usock, &fds); | 
|---|
| 439 |  | 
|---|
| 440 | /* Wait up to 5 seconds */ | 
|---|
| 441 | tv.tv_sec = 5; | 
|---|
| 442 | tv.tv_usec = 0; | 
|---|
| 443 | retval = select((sock > usock ? sock : usock) + 1, | 
|---|
| 444 | &fds, NULL, NULL, &tv); | 
|---|
| 445 | #endif | 
|---|
| 446 | errnr = errno; | 
|---|
| 447 | /* join any handleClient threads that we started and that may | 
|---|
| 448 | * have finished by now */ | 
|---|
| 449 | for (threadp = &threads; *threadp; threadp = &(*threadp)->next) { | 
|---|
| 450 | if ((*threadp)->dead && | 
|---|
| 451 | pthread_join((*threadp)->tid, &e) == 0) { | 
|---|
| 452 | p = *threadp; | 
|---|
| 453 | *threadp = p->next; | 
|---|
| 454 | free(p); | 
|---|
| 455 | if (e != NO_ERR) { | 
|---|
| 456 | Mfprintf(stderr, "client error: %s\n", | 
|---|
| 457 | getErrMsg((char *) e)); | 
|---|
| 458 | freeErr(e); | 
|---|
| 459 | } | 
|---|
| 460 | if (*threadp == NULL) | 
|---|
| 461 | break; | 
|---|
| 462 | } | 
|---|
| 463 | } | 
|---|
| 464 | childhandler(); | 
|---|
| 465 | reinitialize(); | 
|---|
| 466 | if (retval == 0) { | 
|---|
| 467 | /* nothing interesting has happened */ | 
|---|
| 468 | continue; | 
|---|
| 469 | } | 
|---|
| 470 | if (retval == -1) { | 
|---|
| 471 | if (_mero_keep_listening == 0) | 
|---|
| 472 | break; | 
|---|
| 473 | switch (errnr) { | 
|---|
| 474 | case EINTR: | 
|---|
| 475 | /* interrupted */ | 
|---|
| 476 | break; | 
|---|
| 477 | case EMFILE: | 
|---|
| 478 | case ENFILE: | 
|---|
| 479 | case ENOBUFS: | 
|---|
| 480 | case ENOMEM: | 
|---|
| 481 | /* transient failures */ | 
|---|
| 482 | break; | 
|---|
| 483 | default: | 
|---|
| 484 | msg = strerror(errnr); | 
|---|
| 485 | goto error; | 
|---|
| 486 | } | 
|---|
| 487 | continue; | 
|---|
| 488 | } | 
|---|
| 489 | if ( | 
|---|
| 490 | #ifdef HAVE_POLL | 
|---|
| 491 | pfd[0].revents & POLLIN | 
|---|
| 492 | #else | 
|---|
| 493 | FD_ISSET(sock, &fds) | 
|---|
| 494 | #endif | 
|---|
| 495 | ) { | 
|---|
| 496 | isusock = false; | 
|---|
| 497 | if ((msgsock = accept4(sock, (SOCKPTR)0, (socklen_t *) 0, SOCK_CLOEXEC)) == -1) { | 
|---|
| 498 | if (_mero_keep_listening == 0) | 
|---|
| 499 | break; | 
|---|
| 500 | switch (errno) { | 
|---|
| 501 | case EINTR: | 
|---|
| 502 | /* interrupted */ | 
|---|
| 503 | break; | 
|---|
| 504 | case EMFILE: | 
|---|
| 505 | case ENFILE: | 
|---|
| 506 | case ENOBUFS: | 
|---|
| 507 | case ENOMEM: | 
|---|
| 508 | /* transient failures */ | 
|---|
| 509 | break; | 
|---|
| 510 | case ECONNABORTED: | 
|---|
| 511 | /* connection aborted before we began */ | 
|---|
| 512 | break; | 
|---|
| 513 | default: | 
|---|
| 514 | msg = strerror(errno); | 
|---|
| 515 | goto error; | 
|---|
| 516 | } | 
|---|
| 517 | continue; | 
|---|
| 518 | } | 
|---|
| 519 | #if defined(HAVE_FCNTL) && (!defined(SOCK_CLOEXEC) || !defined(HAVE_ACCEPT4)) | 
|---|
| 520 | (void) fcntl(msgsock, F_SETFD, FD_CLOEXEC); | 
|---|
| 521 | #endif | 
|---|
| 522 | } else if ( | 
|---|
| 523 | #ifdef HAVE_POLL | 
|---|
| 524 | pfd[1].revents & POLLIN | 
|---|
| 525 | #else | 
|---|
| 526 | FD_ISSET(usock, &fds) | 
|---|
| 527 | #endif | 
|---|
| 528 | ) { | 
|---|
| 529 | struct msghdr msgh; | 
|---|
| 530 | struct iovec iov; | 
|---|
| 531 | char buf[1]; | 
|---|
| 532 | int rv; | 
|---|
| 533 | char ccmsg[CMSG_SPACE(sizeof(int))]; | 
|---|
| 534 |  | 
|---|
| 535 | isusock = true; | 
|---|
| 536 | if ((msgsock = accept4(usock, (SOCKPTR)0, (socklen_t *)0, SOCK_CLOEXEC)) == -1) { | 
|---|
| 537 | if (_mero_keep_listening == 0) | 
|---|
| 538 | break; | 
|---|
| 539 | switch (errno) { | 
|---|
| 540 | case EINTR: | 
|---|
| 541 | /* interrupted */ | 
|---|
| 542 | break; | 
|---|
| 543 | case EMFILE: | 
|---|
| 544 | case ENFILE: | 
|---|
| 545 | case ENOBUFS: | 
|---|
| 546 | case ENOMEM: | 
|---|
| 547 | /* transient failures */ | 
|---|
| 548 | break; | 
|---|
| 549 | case ECONNABORTED: | 
|---|
| 550 | /* connection aborted before we began */ | 
|---|
| 551 | break; | 
|---|
| 552 | default: | 
|---|
| 553 | msg = strerror(errno); | 
|---|
| 554 | goto error; | 
|---|
| 555 | } | 
|---|
| 556 | continue; | 
|---|
| 557 | } | 
|---|
| 558 | #if defined(HAVE_FCNTL) && (!defined(SOCK_CLOEXEC) || !defined(HAVE_ACCEPT4)) | 
|---|
| 559 | (void) fcntl(usock, F_SETFD, FD_CLOEXEC); | 
|---|
| 560 | #endif | 
|---|
| 561 |  | 
|---|
| 562 | /* BEWARE: unix domain sockets have a slightly different | 
|---|
| 563 | * behaviour initialy than normal sockets, because we can | 
|---|
| 564 | * send filedescriptors or credentials with them.  To do so, | 
|---|
| 565 | * we need to use sendmsg/recvmsg, which operates on a bare | 
|---|
| 566 | * socket.  Unfortunately we *have* to send something, so it | 
|---|
| 567 | * is one byte that can optionally carry the ancillary data. | 
|---|
| 568 | * This byte is at this moment defined to contain a character: | 
|---|
| 569 | *  '0' - there is no ancillary data | 
|---|
| 570 | *  '1' - ancillary data for passing a file descriptor | 
|---|
| 571 | * The future may introduce a state for passing credentials. | 
|---|
| 572 | * Any unknown character must be interpreted as some unknown | 
|---|
| 573 | * action, and hence not supported by the server. | 
|---|
| 574 | * Since there is no reason why one would like to pass | 
|---|
| 575 | * descriptors to Merovingian, this is not implemented here. */ | 
|---|
| 576 |  | 
|---|
| 577 | iov.iov_base = buf; | 
|---|
| 578 | iov.iov_len = 1; | 
|---|
| 579 |  | 
|---|
| 580 | msgh.msg_name = 0; | 
|---|
| 581 | msgh.msg_namelen = 0; | 
|---|
| 582 | msgh.msg_iov = &iov; | 
|---|
| 583 | msgh.msg_iovlen = 1; | 
|---|
| 584 | msgh.msg_control = ccmsg; | 
|---|
| 585 | msgh.msg_controllen = sizeof(ccmsg); | 
|---|
| 586 |  | 
|---|
| 587 | rv = recvmsg(msgsock, &msgh, 0); | 
|---|
| 588 | if (rv == -1) { | 
|---|
| 589 | closesocket(msgsock); | 
|---|
| 590 | continue; | 
|---|
| 591 | } | 
|---|
| 592 |  | 
|---|
| 593 | switch (buf[0]) { | 
|---|
| 594 | case '0': | 
|---|
| 595 | /* nothing special, nothing to do */ | 
|---|
| 596 | break; | 
|---|
| 597 | case '1': | 
|---|
| 598 | /* filedescriptor, no way */ | 
|---|
| 599 | closesocket(msgsock); | 
|---|
| 600 | Mfprintf(stderr, "client error: fd passing not supported\n"); | 
|---|
| 601 | continue; | 
|---|
| 602 | default: | 
|---|
| 603 | /* some unknown state */ | 
|---|
| 604 | closesocket(msgsock); | 
|---|
| 605 | Mfprintf(stderr, "client error: unknown initial byte\n"); | 
|---|
| 606 | continue; | 
|---|
| 607 | } | 
|---|
| 608 | } else | 
|---|
| 609 | continue; | 
|---|
| 610 | /* start handleClient as a thread so that we're not blocked by | 
|---|
| 611 | * a slow client */ | 
|---|
| 612 | data = malloc(sizeof(*data)); /* freed by handleClient */ | 
|---|
| 613 | p = malloc(sizeof(*p)); | 
|---|
| 614 | if (data == NULL || p == NULL) { | 
|---|
| 615 | if (data) | 
|---|
| 616 | free(data); | 
|---|
| 617 | if (p) | 
|---|
| 618 | free(p); | 
|---|
| 619 | closesocket(msgsock); | 
|---|
| 620 | Mfprintf(stderr, "cannot allocate memory\n"); | 
|---|
| 621 | continue; | 
|---|
| 622 | } | 
|---|
| 623 | data->sock = msgsock; | 
|---|
| 624 | data->isusock = isusock; | 
|---|
| 625 | p->dead = 0; | 
|---|
| 626 | data->self = p; | 
|---|
| 627 | data->challenge[31] = '\0'; | 
|---|
| 628 | generateSalt(data->challenge, 31); | 
|---|
| 629 | if (pthread_create(&p->tid, NULL, handleClient, data) == 0) { | 
|---|
| 630 | p->next = threads; | 
|---|
| 631 | threads = p; | 
|---|
| 632 | } else { | 
|---|
| 633 | closesocket(msgsock); | 
|---|
| 634 | free(data); | 
|---|
| 635 | free(p); | 
|---|
| 636 | } | 
|---|
| 637 | } while (_mero_keep_listening); | 
|---|
| 638 | shutdown(sock, SHUT_RDWR); | 
|---|
| 639 | closesocket(sock); | 
|---|
| 640 | return(NO_ERR); | 
|---|
| 641 |  | 
|---|
| 642 | error: | 
|---|
| 643 | _mero_keep_listening = 0; | 
|---|
| 644 | closesocket(sock); | 
|---|
| 645 | return(newErr( "accept connection: %s", msg)); | 
|---|
| 646 | } | 
|---|
| 647 |  | 
|---|
| 648 | /* vim:set ts=4 sw=4 noexpandtab: */ | 
|---|
| 649 |  | 
|---|