/* * Copyright (c) 2019-2022 Job Snijders * Copyright (c) 2018 Peter Schoenmaker * Copyright (c) 2007-2019 Alexandre Snarskii * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" #include "sx_report.h" int debug_expander = 0; int pipelining = 1; int expand_special_asn = 0; static inline int tentry_cmp(struct sx_tentry *a, struct sx_tentry *b) { return strcasecmp(a->text, b->text); } RB_GENERATE_STATIC(tentree, sx_tentry, entry, tentry_cmp); int asn_cmp(struct asn_entry *a, struct asn_entry *b) { return (a->asn < b->asn ? -1 : a->asn > b->asn); } RB_GENERATE(asn_tree, asn_entry, entry, asn_cmp); int bgpq_expander_init(struct bgpq_expander *b, int af) { if (!af) af = AF_INET; if (!b) return 0; memset(b, 0, sizeof(struct bgpq_expander)); b->tree = sx_radix_tree_new(af); if (!b->tree) goto fixups; b->family = af; b->sources = ""; b->usesource = 0; b->name = "NN"; b->aswidth = 8; b->identify = 1; b->server = "rr.ntt.net"; b->port = "43"; RB_INIT(&b->asnlist); STAILQ_INIT(&b->wq); STAILQ_INIT(&b->rq); STAILQ_INIT(&b->rsets); STAILQ_INIT(&b->macroses); return 1; fixups: if (b->tree) sx_radix_tree_freeall(b->tree); b->tree = NULL; free(b); return 0; } int bgpq_expander_add_asset(struct bgpq_expander *b, char *as) { struct slentry *le; if (!b || !as) return 0; le = sx_slentry_new(as); STAILQ_INSERT_TAIL(&b->macroses, le, entry); return 1; } int bgpq_expander_add_rset(struct bgpq_expander *b, char *rs) { struct slentry *le; if (!b || !rs) return 0; le = sx_slentry_new(rs); if (!le) return 0; STAILQ_INSERT_TAIL(&b->rsets, le, entry); return 1; } static int bgpq_expander_add_already(struct bgpq_expander *b, char *rs) { struct sx_tentry *le, lkey; lkey.text = rs; if (RB_FIND(tentree, &b->already, &lkey)) return 0; le = sx_tentry_new(rs); RB_INSERT(tentree, &b->already, le); return 1; } int bgpq_expander_add_stop(struct bgpq_expander *b, char *rs) { struct sx_tentry *le, lkey; lkey.text = rs; if (RB_FIND(tentree, &b->stoplist, &lkey)) return 0; le = sx_tentry_new(rs); RB_INSERT(tentree, &b->stoplist, le); return 1; } int bgpq_expander_add_as(struct bgpq_expander *b, char *as) { char *eoa; uint32_t asno = 0; struct asn_entry *asne; if (!b || !as) return 0; asno = strtoul(as + 2, &eoa, 10); if (eoa && *eoa != 0) { sx_report(SX_ERROR, "Invalid symbol in AS number: '%c' in %s\n", *eoa, as); return 0; } if (!expand_special_asn && (asno == 23456 || asno >= 4200000000ul || (asno >= 64496 && asno <= 65551))) { sx_report(SX_ERROR, "Invalid AS number: %u\n", asno); return 0; } if ((asne = malloc(sizeof(struct asn_entry))) == NULL) err(1, NULL); asne->asn = asno; RB_INSERT(asn_tree, &b->asnlist, asne); return 1; } int bgpq_expander_add_prefix(struct bgpq_expander *b, char *prefix) { struct sx_prefix *p = sx_prefix_alloc(NULL); if (!sx_prefix_parse(p, 0, prefix)) { sx_report(SX_ERROR, "Unable to parse prefix %s\n", prefix); return 0; } else if (p->family != b->family) { SX_DEBUG(debug_expander, "Ignoring prefix %s with wrong " "address family\n", prefix); return 0; } if (b->maxlen && p->masklen>b->maxlen) { SX_DEBUG(debug_expander, "Ignoring prefix %s: masklen %i > max" " masklen %u\n", prefix, p->masklen, b->maxlen); return 0; } sx_radix_tree_insert(b->tree, p); if (p) sx_prefix_free(p); return 1; } int bgpq_expander_add_prefix_range(struct bgpq_expander *b, char *prefix) { return sx_prefix_range_parse(b->tree, b->family, b->maxlen, prefix); } char * bgpq_get_asset(char *object) { char *d, *asset; d = strstr(object, "::"); if (d) d += 2; else d = object; if ((asset = calloc(1, 256)) == NULL) err(1, NULL); memcpy(asset, d, strlen(object) - (d - object)); return asset; } char * bgpq_get_rset(char *object) { char *d, *rset; d = strstr(object, "::"); if (d) d += 2; else d = object; if ((rset = calloc(1, 256)) == NULL) err(1, NULL); memcpy(rset, d, strlen(object) - (d - object)); return rset; } char * bgpq_get_source(char *object) { char *d; char *source; unsigned int slen; d = strstr(object, "::"); if (d) { if ((source = calloc(1, 256)) == NULL) err(1, NULL); slen = d - object; memcpy(source, object, slen); return source; } return NULL; } static int bgpq_expanded_macro(char *as, struct bgpq_expander *ex, struct request *req) { bgpq_expander_add_as(ex, as); return 1; } struct request * bgpq_pipeline(struct bgpq_expander *b, int (*callback)(char *, struct bgpq_expander *b, struct request *req), void *udata, char *fmt, ...); int bgpq_expand_irrd(struct bgpq_expander *b, int (*callback)(char*, struct bgpq_expander *b, struct request *req), void *udata, char *fmt, ...); static int bgpq_expanded_macro_limit(char *as, struct bgpq_expander *b, struct request *req) { char *source; struct request *req1; if (!strncasecmp(as, "AS-", 3) || strchr(as, '-') || strchr(as, ':')) { struct sx_tentry tkey = { .text = as }; if (RB_FIND(tentree, &b->already, &tkey)) { SX_DEBUG(debug_expander > 2, "%s is already expanding, " "ignore\n", as); return 0; } if (RB_FIND(tentree, &b->stoplist, &tkey)) { SX_DEBUG(debug_expander > 2, "%s is in the stoplist, " "ignore\n", as); return 0; } if (!b->maxdepth || (b->cdepth + 1 < b->maxdepth && req->depth + 1 < b->maxdepth)) { bgpq_expander_add_already(b, as); if (pipelining) { if (b->usesource) { source = bgpq_get_source(as); if (source) { bgpq_pipeline(b, NULL, NULL, "!s%s\n", source); free(source); } else { bgpq_pipeline(b, NULL, NULL, "!s%s\n", b->defaultsources); } } req1 = bgpq_pipeline(b, bgpq_expanded_macro_limit, NULL, "!i%s\n", bgpq_get_asset(as)); req1->depth = req->depth + 1; } else { if (b->usesource) { source = bgpq_get_source(as); if (source) { bgpq_expand_irrd(b, NULL, NULL, "!s%s\n", source); free(source); } else { bgpq_expand_irrd(b, NULL, NULL, "!s%s\n", b->defaultsources); } } b->cdepth++; bgpq_expand_irrd(b, bgpq_expanded_macro_limit, NULL, "!i%s\n", bgpq_get_asset(as)); b->cdepth--; } } else { SX_DEBUG(debug_expander > 2, "ignoring %s at depth %i\n", as, b->cdepth ? (b->cdepth + 1) : (req->depth + 1)); } } else if (!strncasecmp(as, "AS", 2)) { struct sx_tentry tkey = { .text = as }; if (RB_FIND(tentree, &b->stoplist, &tkey)) { SX_DEBUG(debug_expander > 2, "%s is in the stoplist, ignore\n", as); return 0; } if (bgpq_expander_add_as(b, as)) { SX_DEBUG(debug_expander > 2, ".. added asn %s\n", as); } else { SX_DEBUG(debug_expander, ".. some error adding as %s " "(in response to %s)\n", as, req->request); } } else if (!strcasecmp(as, "ANY")) return 0; else sx_report(SX_ERROR, "unexpected object '%s' in " "expanded_macro_limit (in response to %s)\n", as, req->request); return 1; } static int bgpq_expanded_prefix(char *as, struct bgpq_expander *ex, struct request *req __attribute__((unused))) { char *d = strchr(as, '^'); if (!d) bgpq_expander_add_prefix(ex, as); else bgpq_expander_add_prefix_range(ex, as); return 1; } static int bgpq_expanded_v6prefix(char *prefix, struct bgpq_expander *ex, struct request *req) { char *d = strchr(prefix, '^'); if (!d) bgpq_expander_add_prefix(ex, prefix); else bgpq_expander_add_prefix_range(ex, prefix); return 1; } static char * bgpq_get_irrd_sources(int fd) { char *query, *response, *sources, *start, *end; const unsigned int rsize = 256; unsigned int slen; int ret, qlen; query = "!s-lc\n"; qlen = strlen(query); if ((response = calloc(1, rsize)) == NULL) err(1, NULL); if ((sources = calloc(1, rsize)) == NULL) err(1, NULL); SX_DEBUG(debug_expander, "Requesting source list %s", query); if ((ret = write(fd, query, strlen(query))) != qlen) { sx_report(SX_ERROR, "Partial write of query to " "IRRd: %i bytes, %s\n", ret, strerror(errno)); close(fd); free(sources); free(response); exit(1); } if (0 < read(fd, response, rsize)) { SX_DEBUG(debug_expander, "Got answer %s", response); if (*(response + strlen(response) - 2) != 'C') { sx_report(SX_ERROR, "Invalid response " "'%s': %s\n", response, query); close(fd); free(sources); free(response); exit(1); } } else { sx_report(SX_ERROR, "failed to read sources\n"); close(fd); free(sources); free(response); exit(1); } start = strchr(response, '\n'); if (start) { start += 1; end = strchr(start, '\n'); if (!end) { sx_report(SX_ERROR, "No 2nd newline in response '%s': %s\n", response, query); close(fd); free(sources); free(response); exit(1); } slen = end - start; if (slen > rsize) memcpy(sources, start, rsize - 1); else memcpy(sources, start, slen); } else { sx_report(SX_ERROR, "No 1st newline in response '%s': %s\n", response, query); close(fd); free(sources); free(response); exit(1); } free(response); return sources; } static struct request * request_alloc(char *request, int (*callback)(char *, struct bgpq_expander *, struct request *), void *udata) { struct request *bp; if ((bp = malloc(sizeof(struct request))) == NULL) err(1, NULL); if (!bp) return NULL; memset(bp, 0, sizeof(struct request)); bp->request = strdup(request); bp->offset = 0; bp->size = strlen(bp->request); bp->callback = callback; bp->udata = udata; return bp; } static void request_free(struct request *req) { if (req->request) free(req->request); free(req); } struct request * bgpq_pipeline(struct bgpq_expander *b, int (*callback)(char *, struct bgpq_expander *, struct request *), void *udata, char *fmt, ...) { struct request *bp = NULL; char request[256]; int ret; va_list ap; va_start(ap, fmt); vsnprintf(request, sizeof(request), fmt, ap); va_end(ap); SX_DEBUG(debug_expander,"expander: sending %s", request); bp = request_alloc(request, callback, udata); if (!bp) { sx_report(SX_FATAL,"Unable to allocate %lu bytes: %s\n", (unsigned long)sizeof(struct request), strerror(errno)); } if (STAILQ_EMPTY(&b->wq)) { ret = write(b->fd, request, bp->size); if (ret < 0) { if (errno == EAGAIN) { STAILQ_INSERT_TAIL(&b->wq, bp, next); return bp; } sx_report(SX_FATAL, "Error writing request: %s\n", strerror(errno)); } bp->offset=ret; if (ret == bp->size) STAILQ_INSERT_TAIL(&b->rq, bp, next); else STAILQ_INSERT_TAIL(&b->wq, bp, next); } else STAILQ_INSERT_TAIL(&b->wq, bp, next); return bp; } static void bgpq_expander_invalidate_asn(struct bgpq_expander *b, const char *q) { char *eptr; unsigned long asn = 0; struct asn_entry find, *res; if (!strncmp(q, "!gas", 4) || !strncmp(q, "!6as", 4)) { errno = 0; asn = strtoul(q + 4, &eptr, 10); if (*eptr != '\n') { sx_report(SX_ERROR, "not a number: %s\n", q); return; } if (errno == ERANGE && asn == ULONG_MAX) { sx_report(SX_ERROR, "overflow: %s\n", q); return; } if (asn == 0 || asn >= 4294967295) { sx_report(SX_ERROR, "out of range: %s\n", q); return; } if (eptr && *eptr != '\n') { sx_report(SX_ERROR, "no number? %s\n", q); return; } find.asn = asn; if ((res = RB_FIND(asn_tree, &b->asnlist, &find)) == NULL) { RB_REMOVE(asn_tree, &b->asnlist, res); free(res); } } } static void bgpq_write(struct bgpq_expander *b) { while(!STAILQ_EMPTY(&b->wq)) { struct request *req = STAILQ_FIRST(&b->wq); int ret = write(b->fd, req->request + req->offset, req->size-req->offset); if (ret < 0) { if (errno == EAGAIN) return; sx_report(SX_FATAL, "error writing data: %s\n", strerror(errno)); } if (ret == req->size - req->offset) { /* this request was dequeued */ STAILQ_REMOVE_HEAD(&b->wq, next); STAILQ_INSERT_TAIL(&b->rq, req, next); } else { req->offset += ret; break; } } } static int bgpq_selread(struct bgpq_expander *b, char *buffer, int size) { fd_set rfd, wfd; int ret; repeat: FD_ZERO(&rfd); FD_SET(b->fd, &rfd); FD_ZERO(&wfd); if (!STAILQ_EMPTY(&b->wq)) FD_SET(b->fd, &wfd); ret = select(b->fd + 1, &rfd, &wfd, NULL, NULL); if (ret == 0) sx_report(SX_FATAL, "select failed\n"); else if (ret == -1 && errno == EINTR) goto repeat; else if (ret == -1) sx_report(SX_FATAL, "select error %i: %s\n", errno, strerror(errno)); if (!STAILQ_EMPTY(&b->wq) && FD_ISSET(b->fd, &wfd)) bgpq_write(b); if (FD_ISSET(b->fd, &rfd)) return read(b->fd, buffer, size); goto repeat; } static int bgpq_read(struct bgpq_expander *b) { static char response[256]; static int off = 0; int rval = 1; if (!STAILQ_EMPTY(&b->wq)) bgpq_write(b); while(!STAILQ_EMPTY(&b->rq)) { int ret = 0; char *cres; struct request *req = STAILQ_FIRST(&b->rq); SX_DEBUG(debug_expander > 2, "waiting for answer to %s," "init %i '%.*s'\n", req->request, off, off, response); if ((cres=strchr(response, '\n')) != NULL) goto have; repeat: ret = bgpq_selread(b, response + off, sizeof(response) - off); if (ret < 0) { if (errno == EAGAIN) goto repeat; sx_report(SX_FATAL,"Error reading data from IRRd: " "%s (dequeue)\n", strerror(errno)); } else if (ret == 0) { sx_report(SX_FATAL,"EOF from IRRd (dequeue)\n"); } off += ret; if (!(cres = strchr(response, '\n'))) goto repeat; have: SX_DEBUG(debug_expander > 5, "got response of %.*s\n", off, response); if (response[0] == 'A') { char *eon, *c; unsigned long offset = 0; unsigned long togot = strtoul(response + 1, &eon, 10); char *recvbuffer = malloc(togot + 2); if (recvbuffer == NULL) err(1, NULL); memset(recvbuffer, 0, togot + 2); if (!eon || *eon != '\n') { sx_report(SX_ERROR,"A-code finished with wrong" " char '%c'(%s)\n", eon ? *eon : '0', response); exit(1); } if ((unsigned)(off - ((eon + 1) - response)) > togot) { // full response and more data is already in buffer memcpy(recvbuffer, eon + 1, togot); offset = togot; memmove(response, eon + 1 + togot, off - ((eon + 1) - response) - togot); off -= togot + ((eon + 1) - response); memset(response + off, 0, sizeof(response) - off); } else { /* response is not yet fully buffered */ memcpy(recvbuffer, eon + 1, off - ((eon + 1) - response)); offset = off - ((eon + 1) - response); memset(response, 0, sizeof(response)); off = 0; } SX_DEBUG(debug_expander > 5, "starting read with ready '%.*s', waiting for " "%lu\n", (int)offset, recvbuffer, togot - offset); if (off > 0) goto have3; if (offset == togot) goto reread2; reread: ret = bgpq_selread(b, recvbuffer + offset, togot - offset); if (ret < 0) { if (errno == EAGAIN) goto reread; sx_report(SX_FATAL,"Error reading IRRd: %s " "(dequeue, result)\n", strerror(errno)); } else if (ret == 0) { sx_report(SX_FATAL,"EOF from IRRd (dequeue, " "result)\n"); } SX_DEBUG(debug_expander > 5, "Read1: got '%.*s'\n", ret, recvbuffer + offset); offset += ret; if (offset < togot) { SX_DEBUG(debug_expander > 5, "expected %lu, got " "%lu expanding %s", togot, strlen(recvbuffer), req->request); goto reread; } reread2: ret = bgpq_selread(b, response + off, sizeof(response) - off); if (ret < 0) { if (errno == EAGAIN) goto reread2; sx_report(SX_FATAL,"Error reading IRRd: %s " "(dequeue,final)\n", strerror(errno)); } else if (ret == 0) { sx_report(SX_FATAL,"EOF from IRRd (dequeue," "final)\n"); } SX_DEBUG(debug_expander > 5, "Read2: got '%.*s'\n", ret, response + off); off += ret; have3: if (!(cres = strchr(response, '\n'))) goto reread2; SX_DEBUG(debug_expander>=3,"Got %s (%lu bytes of %lu) " "in response to %sfinal code: %.*s", recvbuffer, strlen(recvbuffer), togot, req->request, off, response); for (c = recvbuffer; c < recvbuffer + togot;) { size_t spn=strcspn(c," \n"); if (spn) c[spn] = 0; if (c[0] == 0) break; if (!req->callback(c, b, req)) rval = 0; c += spn + 1; } assert(c == recvbuffer + togot); memset(recvbuffer, 0, togot + 2); free(recvbuffer); } else if (response[0] == 'C') { /* No data */ SX_DEBUG(debug_expander,"No data expanding %s", req->request); if (b->validate_asns) bgpq_expander_invalidate_asn(b, req->request); } else if (response[0] == 'D') { SX_DEBUG(debug_expander, "Key not found expanding %s", req->request); if (b->validate_asns) bgpq_expander_invalidate_asn(b, req->request); rval = 0; } else if (response[0] == 'E') { sx_report(SX_ERROR, "Multiple keys expanding %s: %s", req->request, response); rval = 0; } else if ( response[0] == 'F') { sx_report(SX_ERROR, "Error expanding %s: %s", req->request, response); rval = 0; } else { sx_report(SX_ERROR,"Wrong reply: %s to %s", response, req->request); exit(1); } memmove(response, cres + 1, off - ((cres + 1) - response)); off -= (cres + 1) - response; memset(response + off, 0, sizeof(response) - off); SX_DEBUG(debug_expander > 5, "fixed response of %i, %.*s\n", off, off, response); STAILQ_REMOVE_HEAD(&b->rq, next); b->piped--; request_free(req); } return rval; } int bgpq_expand_irrd(struct bgpq_expander *b, int (*callback)(char *, struct bgpq_expander *, struct request *), void *udata, char *fmt, ...) { char request[256], response[256]; va_list ap; ssize_t ret; int off = 0; struct request *req; int rval = 1; va_start(ap, fmt); vsnprintf(request, sizeof(request), fmt, ap); va_end(ap); req = request_alloc(request, callback, udata); SX_DEBUG(debug_expander, "expander sending: %s", request); if ((ret = write(b->fd, request, strlen(request)) == 0) || ret == -1) { sx_report(SX_ERROR, "Partial write of request to IRRd: %li bytes, %s\n", ret, strerror(errno)); exit(1); } memset(response, 0, sizeof(response)); repeat: ret = bgpq_selread(b, response+off, sizeof(response)-off); if (ret < 0) { sx_report(SX_ERROR, "Error reading IRRd: %s\n", strerror(errno)); exit(1); } else if (ret == 0) { sx_report(SX_FATAL, "EOF reading IRRd\n"); } off += ret; if (strchr(response, '\n') == NULL) goto repeat; SX_DEBUG(debug_expander > 2, "expander: initially got %lu bytes, " "'%s'\n", (unsigned long)strlen(response), response); if (response[0] == 'A') { char *eon, *c; long togot = strtoul(response + 1, &eon, 10); char *recvbuffer = malloc(togot + 2); int offset = 0; if (!recvbuffer) { sx_report(SX_FATAL, "Error allocating %lu bytes: %s\n", togot + 2, strerror(errno)); } if (eon && *eon != '\n') { sx_report(SX_ERROR,"A-code finished with wrong char " "'%c' (%s)\n", *eon, response); exit(1); } if (off - ((eon + 1) - response) > togot) { memcpy(recvbuffer, eon + 1, togot); offset = togot; memmove(response, eon + 1 + togot, off - ((eon + 1) - response) - togot); off -= togot + ((eon + 1) - response); memset(response+off, 0, sizeof(response) - off); } else { memcpy(recvbuffer, eon + 1, off - ((eon + 1) - response)); offset = off - ((eon + 1) - response); memset(response, 0, sizeof(response)); off = 0; } if (off > 0) goto have3; if (offset == togot) goto reread2; reread: ret = bgpq_selread(b, recvbuffer + offset, togot - offset); if (ret == 0) { sx_report(SX_FATAL,"EOF from IRRd (expand,result)\n"); } else if (ret < 0) { sx_report(SX_FATAL,"Error reading IRRd: %s " "(expand,result)\n", strerror(errno)); } offset += ret; if (offset < togot) goto reread; reread2: ret = bgpq_selread(b, response+off, sizeof(response) - off); if (ret < 0) { sx_report(SX_FATAL, "error reading IRRd: %s\n", strerror(errno)); } else if (ret == 0) { sx_report(SX_FATAL, "eof reading IRRd\n"); } off += ret; have3: if (!strchr(response, '\n')) goto reread2; SX_DEBUG(debug_expander > 2,"expander: final reply of %lu bytes," " %.*sreturn code %.*s", (unsigned long)strlen(recvbuffer), offset, recvbuffer, off, response); for (c = recvbuffer; c < recvbuffer + togot;) { size_t spn = strcspn(c, " \n"); if (spn) c[spn] = 0; if (c[0] == 0) break; if (callback) if (!callback(c, b, req)) rval = 0; c += spn + 1; } memset(recvbuffer, 0, togot + 2); free(recvbuffer); } else if (response[0] == 'C') { /* no data */ if (b->validate_asns) bgpq_expander_invalidate_asn(b, request); } else if (response[0] == 'D') { SX_DEBUG(debug_expander, "Key not found expanding %s", req->request); if (b->validate_asns) bgpq_expander_invalidate_asn(b, request); rval = 0; } else if (response[0] == 'E') { /* XXXXXX */ } else if (response[0] == 'F') { sx_report(SX_ERROR, "Error expanding %s: %s", request, response); rval = 0; } else { sx_report(SX_ERROR,"Wrong reply: %s", response); exit(1); } request_free(req); return rval; } int bgpq_expand(struct bgpq_expander *b) { char *source; struct slentry *mc; struct addrinfo hints, *res = NULL, *rp; struct linger sl; struct asn_entry *asne; int fd = -1, err, ret, aquery = 0, nodelay = 1; int slen; sl.l_onoff = 1; sl.l_linger = 5; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; err=getaddrinfo(b->server, b->port, &hints, &res); if (err) { sx_report(SX_ERROR,"Unable to resolve %s: %s\n", b->server, gai_strerror(err)); exit(1); } for (rp=res; rp; rp = rp->ai_next) { fd = socket(rp->ai_family, rp->ai_socktype, 0); if (fd == -1) { if (errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) continue; sx_report(SX_ERROR,"Unable to create socket: %s\n", strerror(errno)); exit(1); } if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &sl, sizeof(struct linger))) { sx_report(SX_ERROR,"Unable to set linger on socket: " "%s\n", strerror(errno)); close(fd); exit(1); } err = connect(fd, rp->ai_addr, rp->ai_addrlen); if (err) { close(fd); fd = -1; continue; } if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) == -1) SX_DEBUG(debug_expander, "Unable to set TCP_NODELAY on" " socket: %s\n", strerror(errno)); break; } freeaddrinfo(res); if (fd == -1) { /* all our attempts to connect failed */ sx_report(SX_ERROR,"All attempts to connect %s failed, last" " error: %s\n", b->server, strerror(errno)); exit(1); } b->fd = fd; SX_DEBUG(debug_expander, "Sending '!!' to server to request for the" " connection to remain open\n"); if ((ret = write(fd, "!!\n", 3)) != 3) { sx_report(SX_ERROR, "Partial write of multiple command mode " "to IRRd: %i bytes, %s\n", ret, strerror(errno)); close(fd); exit(1); } if (b->identify) { SX_DEBUG(debug_expander, "b->identify: Sending '!n " PACKAGE_STRING "' to server.\n"); char ident[128]; int ilen = snprintf(ident, sizeof(ident), "!n" PACKAGE_STRING "\n"); if (ilen > 0) { if ((ret = write(fd, ident, ilen)) != ilen) { sx_report(SX_ERROR, "Partial write of " "identifier to IRRd: %i bytes, %s\n", ret, strerror(errno)); close(fd); exit(1); } memset(ident, 0, sizeof(ident)); if (0 < read(fd, ident, sizeof(ident))) { SX_DEBUG(debug_expander, "Got answer %s", ident); } else { sx_report(SX_ERROR, "ident, failed read from IRRd\n"); close(fd); exit(1); } } else { sx_report(SX_ERROR, "snprintf(ident) failed\n"); close(fd); exit(1); } } /* Test whether the server has support for the A query */ if (b->generation >= T_PREFIXLIST && !STAILQ_EMPTY(&b->macroses)) { char aret[128]; char aresp[] = "F Missing required set name for A query"; SX_DEBUG(debug_expander, "Testing support for A queries\n"); if ((ret = write(fd, "!a\n", 3)) != 3) { sx_report(SX_ERROR, "Partial write of '!a' test query " "to IRRd: %i bytes, %s\n", ret, strerror(errno)); close(fd); exit(1); } memset(aret, 0, sizeof(aret)); if (0 < read(fd, aret, sizeof(aret))) { if (strncmp(aret, aresp, strlen(aresp)) == 0) { SX_DEBUG(debug_expander, "Server supports A query\n"); aquery = 1; } else { SX_DEBUG(debug_expander, "No support for A query\n"); } } else { sx_report(SX_ERROR, "A query test failed read from IRRd\n"); close(fd); exit(1); } } if (b->sources && b->sources[0] != 0) { b->defaultsources = strdup(b->sources); } else if (b->usesource) { if (b->sources && b->sources[0] != 0) { b->defaultsources = strdup(b->sources); } else { b->defaultsources = bgpq_get_irrd_sources(b->fd); } } else { b->defaultsources = bgpq_get_irrd_sources(b->fd); } if (b->sources && b->sources[0] != 0) { slen = strlen(b->sources) + 4; if (slen < 256) slen = 256; char sources[slen]; slen = snprintf(sources, sizeof(sources), "!s%s\n", b->sources); if (slen > 0) { SX_DEBUG(debug_expander, "Requesting sources %s", sources); if ((ret = write(fd, sources, slen)) != slen) { sx_report(SX_ERROR, "Partial write of sources to " "IRRd: %i bytes, %s\n", ret, strerror(errno)); close(fd); exit(1); } memset(sources, 0, sizeof(sources)); if (0 < read(fd, sources, sizeof(sources))) { SX_DEBUG(debug_expander, "Got answer %s", sources); if (sources[0] != 'C') { sx_report(SX_ERROR, "Invalid source(s) " "'%s': %s\n", b->sources, sources); close(fd); exit(1); } } else { sx_report(SX_ERROR, "failed to read sources\n"); close(fd); exit(1); } } else { sx_report(SX_ERROR, "snprintf(sources) failed\n"); close(fd); exit(1); } } if (pipelining) fcntl(fd, F_SETFL, O_NONBLOCK|(fcntl(fd, F_GETFL))); STAILQ_FOREACH(mc, &b->macroses, entry) { if (!b->maxdepth && RB_EMPTY(&b->stoplist)) { if (b->usesource) { source = bgpq_get_source(mc->text); if (source){ if (pipelining){ bgpq_pipeline(b, NULL, NULL, "!s%s\n", source); bgpq_pipeline(b, bgpq_expanded_macro_limit, b, "!i%s\n", bgpq_get_asset(mc->text)); } else { bgpq_expand_irrd(b, NULL, NULL, "!s%s\n", source); bgpq_expand_irrd(b, bgpq_expanded_macro_limit, b, "!i%s\n", bgpq_get_asset(mc->text)); } free(source); } else { if (pipelining){ bgpq_pipeline(b, NULL, NULL, "!s%s\n", b->defaultsources); bgpq_pipeline(b, bgpq_expanded_macro_limit, b, "!i%s\n", bgpq_get_asset(mc->text)); } else { bgpq_expand_irrd(b, NULL, NULL, "!s%s\n", b->defaultsources); bgpq_expand_irrd(b, bgpq_expanded_macro_limit, b, "!i%s\n", bgpq_get_asset(mc->text)); } } } else if (aquery) bgpq_expand_irrd(b, bgpq_expanded_prefix, b, "!a%s%s\n", b->family == AF_INET ? "4" : "6", bgpq_get_asset(mc->text)); else bgpq_expand_irrd(b, bgpq_expanded_macro, b, "!i%s,1\n", bgpq_get_asset(mc->text)); } else { bgpq_expander_add_already(b, bgpq_get_asset(mc->text)); if (pipelining) bgpq_pipeline(b, bgpq_expanded_macro_limit, NULL, "!i%s\n", bgpq_get_asset(mc->text)); else bgpq_expand_irrd(b, bgpq_expanded_macro_limit, NULL, "!i%s\n", bgpq_get_asset(mc->text)); } } if (pipelining){ bgpq_pipeline(b, NULL, NULL, "!s%s\n", b->defaultsources); } else { bgpq_expand_irrd(b, NULL, NULL, "!s%s\n", b->defaultsources); } if (pipelining) { if (!STAILQ_EMPTY(&b->wq)) bgpq_write(b); if (!STAILQ_EMPTY(&b->rq)) bgpq_read(b); } if (b->generation >= T_PREFIXLIST || b->validate_asns) { STAILQ_FOREACH(mc, &b->rsets, entry) { if (b->usesource) { source = bgpq_get_source(mc->text); if (source){ if (pipelining){ printf("Checking %s\n", bgpq_get_rset(mc->text)); bgpq_pipeline(b, NULL, NULL, "!s%s\n", source); if (b->family == AF_INET) bgpq_pipeline(b, bgpq_expanded_prefix, NULL, "!i%s\n", bgpq_get_rset(mc->text)); else bgpq_pipeline(b, bgpq_expanded_v6prefix, NULL, "!i%s\n", bgpq_get_rset(mc->text)); } else { bgpq_expand_irrd(b, NULL, NULL, "!s%s\n", source); if (b->family == AF_INET) bgpq_expand_irrd(b, bgpq_expanded_prefix, NULL, "!i%s\n", bgpq_get_rset(mc->text)); else bgpq_expand_irrd(b, bgpq_expanded_v6prefix, NULL, "!i%s\n", bgpq_get_rset(mc->text)); } free(source); } else { if (pipelining){ bgpq_pipeline(b, NULL, NULL, "!s%s\n", b->defaultsources); if (b->family == AF_INET) bgpq_pipeline(b, bgpq_expanded_prefix, NULL, "!i%s\n", bgpq_get_rset(mc->text)); else bgpq_pipeline(b, bgpq_expanded_v6prefix, NULL, "!i%s\n", bgpq_get_rset(mc->text)); } else { bgpq_expand_irrd(b, NULL, NULL, "!s%s\n", b->defaultsources); if (b->family == AF_INET) bgpq_expand_irrd(b, bgpq_expanded_prefix, NULL, "!i%s\n", bgpq_get_rset(mc->text)); else bgpq_expand_irrd(b, bgpq_expanded_v6prefix, NULL, "!i%s\n", bgpq_get_rset(mc->text)); } } } else { if (pipelining){ bgpq_pipeline(b, NULL, NULL, "!s%s\n", b->defaultsources); if (b->family == AF_INET) bgpq_pipeline(b, bgpq_expanded_prefix, NULL, "!i%s,1\n", bgpq_get_rset(mc->text)); else bgpq_pipeline(b, bgpq_expanded_v6prefix, NULL, "!i%s,1\n", bgpq_get_rset(mc->text)); } else { bgpq_expand_irrd(b, NULL, NULL, "!s%s\n", b->defaultsources); if (b->family == AF_INET) bgpq_expand_irrd(b, bgpq_expanded_prefix, NULL, "!i%s,1\n", bgpq_get_rset(mc->text)); else bgpq_expand_irrd(b, bgpq_expanded_v6prefix, NULL, "!i%s,1\n", bgpq_get_rset(mc->text)); } } } RB_FOREACH(asne, asn_tree, &b->asnlist) { if (b->family == AF_INET6) { if (!pipelining) { bgpq_expand_irrd(b, bgpq_expanded_v6prefix, NULL, "!6as%" PRIu32 "\n", asne->asn); } else { bgpq_pipeline(b, bgpq_expanded_v6prefix, NULL, "!6as%" PRIu32 "\n", asne->asn); } } else { if (!pipelining) { bgpq_expand_irrd(b, bgpq_expanded_prefix, NULL, "!gas%" PRIu32 "\n", asne->asn); } else { bgpq_pipeline(b, bgpq_expanded_prefix, NULL, "!gas%" PRIu32 "\n", asne->asn); } } } if (pipelining) { if (!STAILQ_EMPTY(&b->wq)) bgpq_write(b); if (!STAILQ_EMPTY(&b->rq)) bgpq_read(b); } } if ((ret = write(fd, "!q\n", 3)) != 3) { sx_report(SX_ERROR, "Partial write of quit to IRRd: %i bytes, %s\n", ret, strerror(errno)); // not worth exiting due to this } if (pipelining) { int fl = fcntl(fd, F_GETFL); fl &= ~O_NONBLOCK; fcntl(fd, F_SETFL, fl); } close(fd); free(b->defaultsources); return 1; } void sx_radix_node_freeall(struct sx_radix_node *n) { if (n->l != NULL) sx_radix_node_freeall(n->l); if (n->r != NULL) sx_radix_node_freeall(n->r); if (n->son != NULL) sx_radix_node_freeall(n->son); if (n->payload) free(n->payload); sx_prefix_free(n->prefix); free(n); } void sx_radix_tree_freeall(struct sx_radix_tree *t) { if (t->head != NULL) sx_radix_node_freeall(t->head); free(t); } /* XXX: needs cleaning up / figuring out */ void bgpq_prequest_freeall(struct bgpq_prequest *bpr) { } void expander_freeall(struct bgpq_expander *expander) { struct sx_tentry *var, *nxt; struct asn_entry *asne, *asne_next; while (!STAILQ_EMPTY(&expander->macroses)) { struct slentry *n1 = STAILQ_FIRST(&expander->macroses); STAILQ_REMOVE_HEAD(&expander->macroses, entry); free(n1->text); free(n1); } while (!STAILQ_EMPTY(&expander->rsets)) { struct slentry *n1 = STAILQ_FIRST(&expander->rsets); STAILQ_REMOVE_HEAD(&expander->rsets, entry); free(n1->text); free(n1); } for (var = RB_MIN(tentree, &expander->already); var != NULL; var = nxt) { nxt = RB_NEXT(tentree, &expander->already, var); RB_REMOVE(tentree, &expander->already, var); free(var->text); free(var); } for (var = RB_MIN(tentree, &expander->stoplist); var != NULL; var = nxt) { nxt = RB_NEXT(tentree, &expander->stoplist, var); RB_REMOVE(tentree, &expander->stoplist, var); free(var->text); free(var); } for (asne = RB_MIN(asn_tree, &expander->asnlist); asne != NULL; asne = asne_next) { asne_next = RB_NEXT(asn_tree, &expander->asnlist, asne); RB_REMOVE(asn_tree, &expander->asnlist, asne); free(asne); } sx_radix_tree_freeall(expander->tree); bgpq_prequest_freeall(expander->firstpipe); bgpq_prequest_freeall(expander->lastpipe); }