int ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep, uint8_t exchange, uint8_t firstpayload, int response) { struct iked_message resp; struct ike_header *hdr; struct ikev2_payload *pld; struct ibuf *buf, *e = *ep; int ret = -1; if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr, SS_LEN(&sa->sa_peer.addr), &sa->sa_local.addr, SS_LEN(&sa->sa_local.addr), response)) == NULL) goto done; resp.msg_msgid = response ? sa->sa_msgid : ikev2_msg_id(env, sa); /* IKE header */ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SK, exchange, response ? IKEV2_FLAG_RESPONSE : 0)) == NULL) goto done; if ((pld = ikev2_add_payload(buf)) == NULL) goto done; /* Encrypt message and add as an E payload */ if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) { log_debug("%s: encryption failed", __func__); goto done; } if (ibuf_cat(buf, e) != 0) goto done; if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1) goto done; if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) goto done; /* Add integrity checksum (HMAC) */ if (ikev2_msg_integr(env, sa, buf) != 0) { log_debug("%s: integrity checksum failed", __func__); goto done; } resp.msg_data = buf; resp.msg_sa = sa; resp.msg_fd = sa->sa_fd; TAILQ_INIT(&resp.msg_proposals); (void)ikev2_pld_parse(env, hdr, &resp, 0); ret = ikev2_msg_send(env, &resp); done: /* e is cleaned up by the calling function */ *ep = e; ikev2_msg_cleanup(env, &resp); return (ret); }
const char * print_host(struct sockaddr_storage *ss, char *buf, size_t len) { static char sbuf[IKED_CYCLE_BUFFERS][NI_MAXHOST + 7]; static int idx = 0; char pbuf[7]; in_port_t port; if (buf == NULL) { buf = sbuf[idx]; len = sizeof(sbuf[idx]); if (++idx >= IKED_CYCLE_BUFFERS) idx = 0; } if (ss->ss_family == AF_UNSPEC) { strlcpy(buf, "any", len); return (buf); } if (getnameinfo((struct sockaddr *)ss, SS_LEN(ss), buf, len, NULL, 0, NI_NUMERICHOST) != 0) { buf[0] = '\0'; return (NULL); } if ((port = socket_getport(ss)) != 0) { snprintf(pbuf, sizeof(pbuf), ":%d", port); (void)strlcat(buf, pbuf, len); } return (buf); }
/** Find our source address * * Get the IP address to be used as a source address * for sending requests in host order. * * @param rh a handle to parsed configuration * @param lia the local address to listen to * **/ void rc_own_bind_addr(rc_handle *rh, struct sockaddr_storage *lia) { char *txtaddr = rc_conf_str(rh, "bindaddr"); struct addrinfo *info; if (rh->own_bind_addr_set) { memcpy(lia, &rh->own_bind_addr, SS_LEN(&rh->own_bind_addr)); return; } memset(lia, 0, sizeof(*lia)); if (txtaddr == NULL || txtaddr[0] == '*') { ((struct sockaddr_in*)lia)->sin_family = AF_INET; ((struct sockaddr_in*)lia)->sin_addr.s_addr = INADDR_ANY; } else { info = rc_getaddrinfo (txtaddr, PW_AI_PASSIVE); if (info == NULL) { rc_log(LOG_ERR, "rc_own_ipaddress: couldn't get IP address from bindaddr"); ((struct sockaddr_in*)lia)->sin_family = AF_INET; ((struct sockaddr_in*)lia)->sin_addr.s_addr = INADDR_ANY; return; } memcpy(lia, info->ai_addr, info->ai_addrlen); } return; }
static int init_controlfd(struct cfg *cf) { struct sockaddr_un ifsun; struct sockaddr_storage ifsin; char *cp; int i, controlfd, flags, so_rcvbuf; if (cf->stable->umode == 0) { unlink(cmd_sock); memset(&ifsun, '\0', sizeof ifsun); #if defined(HAVE_SOCKADDR_SUN_LEN) ifsun.sun_len = strlen(cmd_sock); #endif ifsun.sun_family = AF_LOCAL; strcpy(ifsun.sun_path, cmd_sock); controlfd = socket(PF_LOCAL, SOCK_STREAM, 0); if (controlfd == -1) err(1, "can't create socket"); setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &controlfd, sizeof controlfd); if (bind(controlfd, sstosa(&ifsun), sizeof ifsun) < 0) err(1, "can't bind to a socket"); if ((cf->stable->run_uname != NULL || cf->stable->run_gname != NULL) && chown(cmd_sock, cf->stable->run_uid, cf->stable->run_gid) == -1) err(1, "can't set owner of the socket"); if (listen(controlfd, 32) != 0) err(1, "can't listen on a socket"); } else { cp = strrchr(cmd_sock, ':'); if (cp != NULL) { *cp = '\0'; cp++; } if (cp == NULL || *cp == '\0') cp = CPORT; cf->stable->port_ctl = atoi(cp); i = (cf->stable->umode == 6) ? AF_INET6 : AF_INET; if (setbindhost(sstosa(&ifsin), i, cmd_sock, cp) != 0) exit(1); controlfd = socket(i, SOCK_DGRAM, 0); if (controlfd == -1) err(1, "can't create socket"); so_rcvbuf = 16 * 1024; if (setsockopt(controlfd, SOL_SOCKET, SO_RCVBUF, &so_rcvbuf, sizeof(so_rcvbuf)) == -1) rtpp_log_ewrite(RTPP_LOG_ERR, cf->stable->glog, "unable to set 16K receive buffer size on controlfd"); if (bind(controlfd, sstosa(&ifsin), SS_LEN(&ifsin)) < 0) err(1, "can't bind to a socket"); } flags = fcntl(controlfd, F_GETFL); fcntl(controlfd, F_SETFL, flags | O_NONBLOCK); return controlfd; }
int cmp_sockaddr( sockaddr_union *ss1, sockaddr_union *ss2, int addr_only) { sockaddr_union tmp1, tmp2; /* if addresses are v4mapped, "unmap" them */ ss1 = unmap_v4mapped(ss1, &tmp1); ss2 = unmap_v4mapped(ss2, &tmp2); if (SU_GET_FAMILY(ss1) == SU_GET_FAMILY(ss2)) { if (addr_only) { #ifdef WORKING_IPV6 if(SU_GET_FAMILY(ss1) == AF_INET6) return memcmp( &ss1->sin6.sin6_addr, &ss2->sin6.sin6_addr, sizeof(ss1->sin6.sin6_addr)); else #endif return memcmp( &ss1->sin.sin_addr, &ss2->sin.sin_addr, sizeof(ss1->sin.sin_addr)); } else { return memcmp(ss1, ss2, SS_LEN(ss1)); } } else { /* compare families to give a total order */ if (SU_GET_FAMILY(ss1) < SU_GET_FAMILY(ss2)) return -1; else return 1; } }
struct ul_opts * rtpp_command_ul_opts_parse(struct cfg *cf, struct rtpp_command *cmd) { int len, tpf, n, i; char c; char *cp, *t; const char *errmsg; struct sockaddr_storage tia; struct ul_opts *ulop; ulop = rtpp_zmalloc(sizeof(struct ul_opts)); if (ulop == NULL) { reply_error(cmd, ECODE_NOMEM_1); goto err_undo_0; } ul_opts_init(cf, ulop); if (cmd->cca.op == UPDATE && cmd->argc > 6) { if (cmd->argc == 8) { ulop->notify_socket = cmd->argv[6]; ulop->notify_tag = cmd->argv[7]; } else { ulop->notify_socket = cmd->argv[5]; ulop->notify_tag = cmd->argv[6]; cmd->cca.to_tag = NULL; } len = url_unquote((uint8_t *)ulop->notify_tag, strlen(ulop->notify_tag)); if (len == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error - invalid URL encoding"); reply_error(cmd, ECODE_PARSE_10); goto err_undo_1; } ulop->notify_tag[len] = '\0'; } ulop->addr = cmd->argv[2]; ulop->port = cmd->argv[3]; /* Process additional command modifiers */ for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) { switch (*cp) { case 'a': case 'A': ulop->asymmetric = 1; break; case 'e': case 'E': if (ulop->lidx < 0 || cf->stable->bindaddr[1] == NULL) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_11); goto err_undo_1; } ulop->lia[ulop->lidx] = cf->stable->bindaddr[1]; ulop->lidx--; break; case 'i': case 'I': if (ulop->lidx < 0 || cf->stable->bindaddr[1] == NULL) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_12); goto err_undo_1; } ulop->lia[ulop->lidx] = cf->stable->bindaddr[0]; ulop->lidx--; break; case '6': ulop->pf = AF_INET6; break; case 's': case 'S': ulop->asymmetric = 0; break; case 'w': case 'W': ulop->weak = 1; break; case 'z': case 'Z': ulop->requested_ptime = strtol(cp + 1, &cp, 10); if (ulop->requested_ptime <= 0) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_13); goto err_undo_1; } cp--; break; case 'c': case 'C': cp += 1; for (t = cp; *cp != '\0'; cp++) { if (!isdigit(*cp) && *cp != ',') break; } if (t == cp) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_14); goto err_undo_1; } ulop->codecs = malloc(cp - t + 1); if (ulop->codecs == NULL) { reply_error(cmd, ECODE_NOMEM_2); goto err_undo_1; } memcpy(ulop->codecs, t, cp - t); ulop->codecs[cp - t] = '\0'; cp--; break; case 'l': case 'L': len = extractaddr(cp + 1, &t, &cp, &tpf); if (len == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_15); goto err_undo_1; } c = t[len]; t[len] = '\0'; ulop->local_addr = host2bindaddr(cf, t, tpf, &errmsg); if (ulop->local_addr == NULL) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "invalid local address: %s: %s", t, errmsg); reply_error(cmd, ECODE_INVLARG_1); goto err_undo_1; } t[len] = c; cp--; break; case 'r': case 'R': len = extractaddr(cp + 1, &t, &cp, &tpf); if (len == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "command syntax error"); reply_error(cmd, ECODE_PARSE_16); goto err_undo_1; } c = t[len]; t[len] = '\0'; ulop->local_addr = alloca(sizeof(struct sockaddr_storage)); n = resolve(ulop->local_addr, tpf, t, SERVICE, AI_PASSIVE); if (n != 0) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "invalid remote address: %s: %s", t, gai_strerror(n)); reply_error(cmd, ECODE_INVLARG_2); goto err_undo_1; } if (local4remote(ulop->local_addr, satoss(ulop->local_addr)) == -1) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "can't find local address for remote address: %s", t); reply_error(cmd, ECODE_INVLARG_3); goto err_undo_1; } ulop->local_addr = addr2bindaddr(cf, ulop->local_addr, &errmsg); if (ulop->local_addr == NULL) { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "invalid local address: %s", errmsg); reply_error(cmd, ECODE_INVLARG_4); goto err_undo_1; } t[len] = c; cp--; break; case 'n': case 'N': ulop->new_port = 1; break; default: RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "unknown command modifier `%c'", *cp); break; } } if (ulop->addr != NULL && ulop->port != NULL && strlen(ulop->addr) >= 7) { n = resolve(sstosa(&tia), ulop->pf, ulop->addr, ulop->port, AI_NUMERICHOST); if (n == 0) { if (!ishostnull(sstosa(&tia))) { for (i = 0; i < 2; i++) { ulop->ia[i] = malloc(SS_LEN(&tia)); if (ulop->ia[i] == NULL) { reply_error(cmd, ECODE_NOMEM_3); goto err_undo_1; } memcpy(ulop->ia[i], &tia, SS_LEN(&tia)); } /* Set port for RTCP, will work both for IPv4 and IPv6 */ n = ntohs(satosin(ulop->ia[1])->sin_port); satosin(ulop->ia[1])->sin_port = htons(n + 1); } } else { RTPP_LOG(cf->stable->glog, RTPP_LOG_ERR, "getaddrinfo(pf=%d, addr=%s, port=%s): %s", ulop->pf, ulop->addr, ulop->port, gai_strerror(n)); } } return (ulop); err_undo_1: rtpp_command_ul_opts_free(ulop); err_undo_0: return (NULL); }
/* like sec_accept, but first it gets the remote system's hostname */ static void ssh_accept( const security_driver_t *driver, char *(*conf_fn)(char *, void *), int in, int out, void (*fn)(security_handle_t *, pkt_t *), void *datap) { struct sec_handle *rh; struct tcp_conn *rc = sec_tcp_conn_get(NULL, "", 0); char *ssh_connection, *p; char *errmsg = NULL; sockaddr_union addr; int result; /* "Accepting" an SSH connection means that amandad was invoked via sshd, so * we should have anSSH_CONNECTION env var. If not, then this probably isn't * a real SSH connection and we should bail out. */ ssh_connection = getenv("SSH_CONNECTION"); if (!ssh_connection) { errmsg = g_strdup("$SSH_CONNECTION not set - was amandad started by sshd?"); goto error; } /* make a local copy, to munge */ ssh_connection = g_strdup(ssh_connection); /* strip off the first component - the ASCII IP address */ if ((p = strchr(ssh_connection, ' ')) == NULL) { errmsg = g_strdup("$SSH_CONNECTION malformed"); goto error; } *p = '\0'; /* ---- everything from here on is just a warning, leaving hostname at "" */ SU_INIT(&addr, AF_INET); /* turn the string address into a sockaddr */ if ((result = str_to_sockaddr(ssh_connection, &addr)) != 1) { if (result == 0) { g_warning("Could not parse peer address %s", ssh_connection); } else { g_warning("Parsing peer address %s: %s", ssh_connection, gai_strerror(result)); } goto done; } /* find the hostname */ result = getnameinfo((struct sockaddr *)&addr, SS_LEN(&addr), rc->hostname, sizeof(rc->hostname), NULL, 0, 0); if (result != 0) { g_warning("Could not get hostname for SSH client %s: %s", ssh_connection, gai_strerror(result)); goto done; } /* and verify it */ if (check_name_give_sockaddr(rc->hostname, (struct sockaddr *)&addr, &errmsg) < 0) { rc->hostname[0] = '\0'; /* null out the bad hostname */ g_warning("Checking SSH client DNS: %s", errmsg); amfree(errmsg); goto done; } done: g_free(ssh_connection); rc->read = in; rc->write = out; rc->accept_fn = fn; rc->driver = driver; rc->conf_fn = conf_fn; rc->datap = datap; sec_tcp_conn_read(rc); return; error: if (ssh_connection) g_free(ssh_connection); /* make up a fake handle for the error */ rh = g_new0(struct sec_handle, 1); security_handleinit(&rh->sech, driver); security_seterror((security_handle_t*)rh, "ssh_accept: %s", errmsg); amfree(errmsg); (*fn)(&rh->sech, NULL); }
int handle_command(struct cfg *cf, int controlfd, double dtime) { int len, argc, i, pidx, asymmetric; int external, pf, lidx, playcount, weak, tpf; int fds[2], lport, n; socklen_t rlen; char buf[1024 * 8]; char *cp, *call_id, *from_tag, *to_tag, *addr, *port, *cookie; char *pname, *codecs, *recording_name, *t; struct rtpp_session *spa, *spb; char **ap, *argv[10]; const char *rname, *errmsg; struct sockaddr *ia[2], *lia[2]; struct sockaddr_storage raddr; int requested_nsamples; enum {DELETE, RECORD, PLAY, NOPLAY, COPY, UPDATE, LOOKUP, QUERY} op; int max_argc; char *socket_name_u, *notify_tag; struct sockaddr *local_addr; char c; requested_nsamples = -1; ia[0] = ia[1] = NULL; spa = spb = NULL; lia[0] = lia[1] = cf->bindaddr[0]; lidx = 1; fds[0] = fds[1] = -1; recording_name = NULL; socket_name_u = notify_tag = NULL; local_addr = NULL; codecs = NULL; if (cf->umode == 0) { for (;;) { len = read(controlfd, buf, sizeof(buf) - 1); if (len != -1 || (errno != EAGAIN && errno != EINTR)) break; sched_yield(); } } else { rlen = sizeof(raddr); len = recvfrom(controlfd, buf, sizeof(buf) - 1, 0, sstosa(&raddr), &rlen); } if (len == -1) { if (errno != EAGAIN && errno != EINTR) rtpp_log_ewrite(RTPP_LOG_ERR, cf->glog, "can't read from control socket"); return -1; } buf[len] = '\0'; rtpp_log_write(RTPP_LOG_DBUG, cf->glog, "received command \"%s\"", buf); cp = buf; argc = 0; memset(argv, 0, sizeof(argv)); for (ap = argv; (*ap = rtpp_strsep(&cp, "\r\n\t ")) != NULL;) if (**ap != '\0') { argc++; if (++ap >= &argv[10]) break; } cookie = NULL; if (argc < 1 || (cf->umode != 0 && argc < 2)) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 0); return 0; } /* Stream communication mode doesn't use cookie */ if (cf->umode != 0) { cookie = argv[0]; for (i = 1; i < argc; i++) argv[i - 1] = argv[i]; argc--; argv[argc] = NULL; } else { cookie = NULL; } addr = port = NULL; switch (argv[0][0]) { case 'u': case 'U': /* U[opts] callid remote_ip remote_port from_tag [to_tag] */ op = UPDATE; rname = "update/create"; break; case 'l': case 'L': op = LOOKUP; rname = "lookup"; break; case 'd': case 'D': op = DELETE; rname = "delete"; break; case 'p': case 'P': /* * P callid pname codecs from_tag to_tag * * <codecs> could be either comma-separated list of supported * payload types or word "session" (without quotes), in which * case list saved on last session update will be used instead. */ op = PLAY; rname = "play"; playcount = 1; pname = argv[2]; codecs = argv[3]; break; case 'r': case 'R': op = RECORD; rname = "record"; break; case 'c': case 'C': op = COPY; rname = "copy"; break; case 's': case 'S': op = NOPLAY; rname = "noplay"; break; case 'v': case 'V': if (argv[0][1] == 'F' || argv[0][1] == 'f') { int i, known; /* * Wait for protocol version datestamp and check whether we * know it. */ if (argc != 2 && argc != 3) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 2); return 0; } /* * Only list 20081224 protocol mod as supported if * user actually enabled notification with -n */ if (strcmp(argv[1], "20081224") == 0 && cf->timeout_handler.socket_name == NULL) { reply_number(cf, controlfd, &raddr, rlen, cookie, 0); return 0; } for (known = i = 0; proto_caps[i].pc_id != NULL; ++i) { if (!strcmp(argv[1], proto_caps[i].pc_id)) { known = 1; break; } } reply_number(cf, controlfd, &raddr, rlen, cookie, known); return 0; } if (argc != 1 && argc != 2) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 2); return 0; } /* This returns base version. */ reply_number(cf, controlfd, &raddr, rlen, cookie, CPROTOVER); return 0; case 'i': case 'I': if (cookie == NULL) len = sprintf(buf, "sessions created: %llu\nactive sessions: %d\n" "active streams: %d\n", cf->sessions_created, cf->sessions_active, cf->nsessions / 2); else len = sprintf(buf, "%s sessions created: %llu\nactive sessions: %d\n" "active streams: %d\n", cookie, cf->sessions_created, cf->sessions_active, cf->nsessions / 2); for (i = 1; i < cf->nsessions; i++) { char addrs[4][256]; spa = cf->sessions[i]; if (spa == NULL || spa->sidx[0] != i) continue; /* RTCP twin session */ if (spa->rtcp == NULL) { spb = spa->rtp; buf[len++] = '\t'; } else { spb = spa->rtcp; buf[len++] = '\t'; buf[len++] = 'C'; buf[len++] = ' '; } addr2char_r(spb->laddr[1], addrs[0], sizeof(addrs[0])); if (spb->addr[1] == NULL) { strcpy(addrs[1], "NONE"); } else { sprintf(addrs[1], "%s:%d", addr2char(spb->addr[1]), addr2port(spb->addr[1])); } addr2char_r(spb->laddr[0], addrs[2], sizeof(addrs[2])); if (spb->addr[0] == NULL) { strcpy(addrs[3], "NONE"); } else { sprintf(addrs[3], "%s:%d", addr2char(spb->addr[0]), addr2port(spb->addr[0])); } len += sprintf(buf + len, "%s/%s: caller = %s:%d/%s, callee = %s:%d/%s, " "stats = %lu/%lu/%lu/%lu, ttl = %d/%d\n", spb->call_id, spb->tag, addrs[0], spb->ports[1], addrs[1], addrs[2], spb->ports[0], addrs[3], spa->pcount[0], spa->pcount[1], spa->pcount[2], spa->pcount[3], spb->ttl[0], spb->ttl[1]); if (len + 512 > sizeof(buf)) { doreply(cf, controlfd, buf, len, &raddr, rlen); len = 0; } } if (len > 0) doreply(cf, controlfd, buf, len, &raddr, rlen);; return 0; break; case 'q': case 'Q': op = QUERY; rname = "query"; break; case 'x': case 'X': /* Delete all active sessions */ rtpp_log_write(RTPP_LOG_INFO, cf->glog, "deleting all active sessions"); for (i = 1; i < cf->nsessions; i++) { spa = cf->sessions[i]; if (spa == NULL || spa->sidx[0] != i) continue; /* Skip RTCP twin session */ if (spa->rtcp != NULL) { remove_session(cf, spa); } } reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; break; default: rtpp_log_write(RTPP_LOG_ERR, cf->glog, "unknown command"); reply_error(cf, controlfd, &raddr, rlen, cookie, 3); return 0; } call_id = argv[1]; if (op == UPDATE || op == LOOKUP || op == PLAY) { max_argc = (op == UPDATE ? 8 : 6); if (argc < 5 || argc > max_argc) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 4); return 0; } from_tag = argv[4]; to_tag = argv[5]; if (op == PLAY && argv[0][1] != '\0') playcount = atoi(argv[0] + 1); if (op == UPDATE && argc > 6) { socket_name_u = argv[6]; if (strncmp("unix:", socket_name_u, 5) == 0) socket_name_u += 5; if (argc == 8) { notify_tag = argv[7]; len = url_unquote((uint8_t *)notify_tag, strlen(notify_tag)); if (len == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error - invalid URL encoding"); reply_error(cf, controlfd, &raddr, rlen, cookie, 4); return 0; } notify_tag[len] = '\0'; } } } if (op == COPY) { if (argc < 4 || argc > 5) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } recording_name = argv[2]; from_tag = argv[3]; to_tag = argv[4]; } if (op == DELETE || op == RECORD || op == NOPLAY || op == QUERY) { if (argc < 3 || argc > 4) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } from_tag = argv[2]; to_tag = argv[3]; } if (op == DELETE || op == RECORD || op == COPY || op == NOPLAY) { /* D, R and S commands don't take any modifiers */ if (argv[0][1] != '\0') { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } } if (op == UPDATE || op == LOOKUP || op == DELETE) { addr = argv[2]; port = argv[3]; /* Process additional command modifiers */ external = 1; /* In bridge mode all clients are assumed to be asymmetric */ asymmetric = (cf->bmode != 0) ? 1 : 0; pf = AF_INET; weak = 0; for (cp = argv[0] + 1; *cp != '\0'; cp++) { switch (*cp) { case 'a': case 'A': asymmetric = 1; break; case 'e': case 'E': if (lidx < 0) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } lia[lidx] = cf->bindaddr[1]; lidx--; break; case 'i': case 'I': if (lidx < 0) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } lia[lidx] = cf->bindaddr[0]; lidx--; break; case '6': pf = AF_INET6; break; case 's': case 'S': asymmetric = 0; break; case 'w': case 'W': weak = 1; break; case 'z': case 'Z': requested_nsamples = (strtol(cp + 1, &cp, 10) / 10) * 80; if (requested_nsamples <= 0) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } cp--; break; case 'c': case 'C': cp += 1; for (t = cp; *cp != '\0'; cp++) { if (!isdigit(*cp) && *cp != ',') break; } if (t == cp) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } codecs = alloca(cp - t + 1); memcpy(codecs, t, cp - t); codecs[cp - t] = '\0'; cp--; break; case 'l': case 'L': len = extractaddr(cp + 1, &t, &cp, &tpf); if (len == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } c = t[len]; t[len] = '\0'; local_addr = host2bindaddr(cf, t, tpf, &errmsg); if (local_addr == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "invalid local address: %s: %s", t, errmsg); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } t[len] = c; cp--; break; case 'r': case 'R': len = extractaddr(cp + 1, &t, &cp, &tpf); if (len == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error"); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } c = t[len]; t[len] = '\0'; local_addr = alloca(sizeof(struct sockaddr_storage)); n = resolve(local_addr, tpf, t, SERVICE, AI_PASSIVE); if (n != 0) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "invalid remote address: %s: %s", t, gai_strerror(n)); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } if (local4remote(cf, local_addr, satoss(local_addr)) == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "can't find local address for remote address: %s", t); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } local_addr = addr2bindaddr(cf, local_addr, &errmsg); if (local_addr == NULL) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "invalid local address: %s", errmsg); reply_error(cf, controlfd, &raddr, rlen, cookie, 1); return 0; } t[len] = c; cp--; break; default: rtpp_log_write(RTPP_LOG_ERR, cf->glog, "unknown command modifier `%c'", *cp); break; } } if (op != DELETE && addr != NULL && port != NULL && strlen(addr) >= 7) { struct sockaddr_storage tia; if ((n = resolve(sstosa(&tia), pf, addr, port, AI_NUMERICHOST)) == 0) { if (!ishostnull(sstosa(&tia))) { for (i = 0; i < 2; i++) { ia[i] = malloc(SS_LEN(&tia)); if (ia[i] == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 5, ia, fds, spa, spb); return 0; } memcpy(ia[i], &tia, SS_LEN(&tia)); } /* Set port for RTCP, will work both for IPv4 and IPv6 */ n = ntohs(satosin(ia[1])->sin_port); satosin(ia[1])->sin_port = htons(n + 1); } } else { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "getaddrinfo: %s", gai_strerror(n)); } } } /* * Record and delete need special handling since they apply to all * streams in the session. */ switch (op) { case DELETE: i = handle_delete(cf, call_id, from_tag, to_tag, weak); break; case RECORD: i = handle_record(cf, call_id, from_tag, to_tag); break; default: i = find_stream(cf, call_id, from_tag, to_tag, &spa); if (i != -1 && op != UPDATE) i = NOT(i); break; } if (i == -1 && op != UPDATE) { rtpp_log_write(RTPP_LOG_INFO, cf->glog, "%s request failed: session %s, tags %s/%s not found", rname, call_id, from_tag, to_tag != NULL ? to_tag : "NONE"); if (op == LOOKUP) { for (i = 0; i < 2; i++) if (ia[i] != NULL) free(ia[i]); reply_port(cf, controlfd, &raddr, rlen, cookie, 0, lia); return 0; } reply_error(cf, controlfd, &raddr, rlen, cookie, 8); return 0; } switch (op) { case DELETE: case RECORD: reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; case NOPLAY: handle_noplay(cf, spa, i); reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; case PLAY: handle_noplay(cf, spa, i); if (strcmp(codecs, "session") == 0) { if (spa->codecs[i] == NULL) { reply_error(cf, controlfd, &raddr, rlen, cookie, 6); return 0; } codecs = spa->codecs[i]; } if (playcount != 0 && handle_play(cf, spa, i, codecs, pname, playcount) != 0) { reply_error(cf, controlfd, &raddr, rlen, cookie, 6); return 0; } reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; case COPY: handle_copy(cf, spa, i, recording_name); reply_ok(cf, controlfd, &raddr, rlen, cookie); return 0; case QUERY: handle_query(cf, controlfd, &raddr, rlen, cookie, spa, i); return 0; case LOOKUP: case UPDATE: /* those are handled below */ break; default: /* Programmatic error, should not happen */ abort(); } pidx = 1; lport = 0; if (i != -1) { assert(op == UPDATE || op == LOOKUP); if (spa->fds[i] == -1) { if (local_addr != NULL) { spa->laddr[i] = local_addr; } if (create_listener(cf, spa->laddr[i], &lport, fds) == -1) { rtpp_log_write(RTPP_LOG_ERR, spa->log, "can't create listener"); reply_error(cf, controlfd, &raddr, rlen, cookie, 7); return 0; } assert(spa->fds[i] == -1); spa->fds[i] = fds[0]; assert(spa->rtcp->fds[i] == -1); spa->rtcp->fds[i] = fds[1]; spa->ports[i] = lport; spa->rtcp->ports[i] = lport + 1; spa->complete = spa->rtcp->complete = 1; append_session(cf, spa, i); append_session(cf, spa->rtcp, i); } if (weak) spa->weak[i] = 1; else if (op == UPDATE) spa->strong = 1; lport = spa->ports[i]; lia[0] = spa->laddr[i]; pidx = (i == 0) ? 1 : 0; spa->ttl_mode = cf->ttl_mode; spa->ttl[0] = cf->max_ttl; spa->ttl[1] = cf->max_ttl; if (op == UPDATE) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "adding %s flag to existing session, new=%d/%d/%d", weak ? ( i ? "weak[1]" : "weak[0]" ) : "strong", spa->strong, spa->weak[0], spa->weak[1]); } rtpp_log_write(RTPP_LOG_INFO, spa->log, "lookup on ports %d/%d, session timer restarted", spa->ports[0], spa->ports[1]); } else { assert(op == UPDATE); rtpp_log_write(RTPP_LOG_INFO, cf->glog, "new session %s, tag %s requested, type %s", call_id, from_tag, weak ? "weak" : "strong"); if (local_addr != NULL) { lia[0] = lia[1] = local_addr; if (lia[0] == NULL) { rtpp_log_write(RTPP_LOG_ERR, spa->log, "can't create listener: %s", t); reply_error(cf, controlfd, &raddr, rlen, cookie, 10); return 0; } } if (create_listener(cf, lia[0], &lport, fds) == -1) { rtpp_log_write(RTPP_LOG_ERR, cf->glog, "can't create listener"); reply_error(cf, controlfd, &raddr, rlen, cookie, 10); return 0; } /* * Session creation. If creation is requested with weak flag, * set weak[0]. */ spa = malloc(sizeof(*spa)); if (spa == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 11, ia, fds, spa, spb); return 0; } /* spb is RTCP twin session for this one. */ spb = malloc(sizeof(*spb)); if (spb == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 12, ia, fds, spa, spb); return 0; } memset(spa, 0, sizeof(*spa)); memset(spb, 0, sizeof(*spb)); for (i = 0; i < 2; i++) { spa->fds[i] = spb->fds[i] = -1; spa->last_update[i] = 0; spb->last_update[i] = 0; } spa->call_id = strdup(call_id); if (spa->call_id == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 13, ia, fds, spa, spb); return 0; } spb->call_id = spa->call_id; spa->tag = strdup(from_tag); if (spa->tag == NULL) { handle_nomem(cf, controlfd, &raddr, rlen, cookie, 14, ia, fds, spa, spb); return 0; } spb->tag = spa->tag; for (i = 0; i < 2; i++) { spa->rrcs[i] = NULL; spb->rrcs[i] = NULL; spa->laddr[i] = lia[i]; spb->laddr[i] = lia[i]; } spa->strong = spa->weak[0] = spa->weak[1] = 0; if (weak) spa->weak[0] = 1; else spa->strong = 1; assert(spa->fds[0] == -1); spa->fds[0] = fds[0]; assert(spb->fds[0] == -1); spb->fds[0] = fds[1]; spa->ports[0] = lport; spb->ports[0] = lport + 1; spa->ttl[0] = cf->max_ttl; spa->ttl[1] = cf->max_ttl; spb->ttl[0] = -1; spb->ttl[1] = -1; spa->log = rtpp_log_open(cf, "rtpproxy", spa->call_id, 0); spb->log = spa->log; spa->rtcp = spb; spb->rtcp = NULL; spa->rtp = NULL; spb->rtp = spa; spa->sridx = spb->sridx = -1; append_session(cf, spa, 0); append_session(cf, spa, 1); append_session(cf, spb, 0); append_session(cf, spb, 1); hash_table_append(cf, spa); cf->sessions_created++; cf->sessions_active++; /* * Each session can consume up to 5 open file descriptors (2 RTP, * 2 RTCP and 1 logging) so that warn user when he is likely to * exceed 80% mark on hard limit. */ if (cf->sessions_active > (cf->nofile_limit.rlim_max * 80 / (100 * 5)) && cf->nofile_limit_warned == 0) { cf->nofile_limit_warned = 1; rtpp_log_write(RTPP_LOG_WARN, cf->glog, "passed 80%% " "threshold on the open file descriptors limit (%d), " "consider increasing the limit using -L command line " "option", (int)cf->nofile_limit.rlim_max); } rtpp_log_write(RTPP_LOG_INFO, spa->log, "new session on a port %d created, " "tag %s", lport, from_tag); if (cf->record_all != 0) { handle_copy(cf, spa, 0, NULL); handle_copy(cf, spa, 1, NULL); } } if (op == UPDATE) { if (cf->timeout_handler.socket_name == NULL && socket_name_u != NULL) rtpp_log_write(RTPP_LOG_ERR, spa->log, "must permit notification socket with -n"); if (spa->timeout_data.notify_tag != NULL) { free(spa->timeout_data.notify_tag); spa->timeout_data.notify_tag = NULL; } if (cf->timeout_handler.socket_name != NULL && socket_name_u != NULL) { if (strcmp(cf->timeout_handler.socket_name, socket_name_u) != 0) { rtpp_log_write(RTPP_LOG_ERR, spa->log, "invalid socket name %s", socket_name_u); socket_name_u = NULL; } else { rtpp_log_write(RTPP_LOG_INFO, spa->log, "setting timeout handler"); spa->timeout_data.handler = &cf->timeout_handler; spa->timeout_data.notify_tag = strdup(notify_tag); } } else if (socket_name_u == NULL && spa->timeout_data.handler != NULL) { spa->timeout_data.handler = NULL; rtpp_log_write(RTPP_LOG_INFO, spa->log, "disabling timeout handler"); } } if (ia[0] != NULL && ia[1] != NULL) { if (spa->addr[pidx] != NULL) spa->last_update[pidx] = dtime; if (spa->rtcp->addr[pidx] != NULL) spa->rtcp->last_update[pidx] = dtime; /* * Unless the address provided by client historically * cannot be trusted and address is different from one * that we recorded update it. */ if (spa->untrusted_addr[pidx] == 0 && !(spa->addr[pidx] != NULL && SA_LEN(ia[0]) == SA_LEN(spa->addr[pidx]) && memcmp(ia[0], spa->addr[pidx], SA_LEN(ia[0])) == 0)) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "pre-filling %s's address " "with %s:%s", (pidx == 0) ? "callee" : "caller", addr, port); if (spa->addr[pidx] != NULL) { if (spa->canupdate[pidx] == 0) { if (spa->prev_addr[pidx] != NULL) free(spa->prev_addr[pidx]); spa->prev_addr[pidx] = spa->addr[pidx]; } else { free(spa->addr[pidx]); } } spa->addr[pidx] = ia[0]; ia[0] = NULL; } if (spa->rtcp->untrusted_addr[pidx] == 0 && !(spa->rtcp->addr[pidx] != NULL && SA_LEN(ia[1]) == SA_LEN(spa->rtcp->addr[pidx]) && memcmp(ia[1], spa->rtcp->addr[pidx], SA_LEN(ia[1])) == 0)) { if (spa->rtcp->addr[pidx] != NULL) { if (spa->rtcp->canupdate[pidx] == 0) { if (spa->rtcp->prev_addr[pidx] != NULL) free(spa->rtcp->prev_addr[pidx]); spa->rtcp->prev_addr[pidx] = spa->rtcp->addr[pidx]; } else { free(spa->rtcp->addr[pidx]); } } spa->rtcp->addr[pidx] = ia[1]; ia[1] = NULL; } } spa->asymmetric[pidx] = spa->rtcp->asymmetric[pidx] = asymmetric; spa->canupdate[pidx] = spa->rtcp->canupdate[pidx] = NOT(asymmetric); if (spa->codecs[pidx] != NULL) { free(spa->codecs[pidx]); spa->codecs[pidx] = NULL; } if (codecs != NULL) spa->codecs[pidx] = strdup(codecs); if (requested_nsamples > 0) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "RTP packets from %s " "will be resized to %d milliseconds", (pidx == 0) ? "callee" : "caller", requested_nsamples / 8); } else if (spa->resizers[pidx].output_nsamples > 0) { rtpp_log_write(RTPP_LOG_INFO, spa->log, "Resizing of RTP " "packets from %s has been disabled", (pidx == 0) ? "callee" : "caller"); } spa->resizers[pidx].output_nsamples = requested_nsamples; for (i = 0; i < 2; i++) if (ia[i] != NULL) free(ia[i]); assert(lport != 0); reply_port(cf, controlfd, &raddr, rlen, cookie, lport, lia); return 0; }
static int do_directtcp_connect( XferElementGlue *self, DirectTCPAddr *addrs) { XferElement *elt = XFER_ELEMENT(self); sockaddr_union addr; int sock; #ifdef WORKING_IPV6 char strsockaddr[INET6_ADDRSTRLEN + 20]; #else char strsockaddr[INET_ADDRSTRLEN + 20]; #endif if (!addrs) { g_debug("element-glue got no directtcp addresses to connect to!"); if (!elt->cancelled) { xfer_cancel_with_error(elt, "%s got no directtcp addresses to connect to", xfer_element_repr(elt)); } goto cancel_wait; } /* set up the sockaddr -- IPv4 only */ copy_sockaddr(&addr, addrs); str_sockaddr_r(&addr, strsockaddr, sizeof(strsockaddr)); if (strncmp(strsockaddr,"255.255.255.255:", 16) == 0) { char buffer[32770]; char *s; int size; char *data_host; int data_port; g_debug("do_directtcp_connect making indirect data connection to %s", strsockaddr); data_port = SU_GET_PORT(&addr); sock = stream_client(NULL, "localhost", data_port, STREAM_BUFSIZE, 0, NULL, 0); if (sock < 0) { xfer_cancel_with_error(elt, "stream_client(): %s", strerror(errno)); goto cancel_wait; } size = full_read(sock, buffer, 32768); if (size < 0 ) { xfer_cancel_with_error(elt, "failed to read from indirecttcp: %s", strerror(errno)); goto cancel_wait; } close(sock); buffer[size++] = ' '; buffer[size] = '\0'; if ((s = strchr(buffer, ':')) == NULL) { xfer_cancel_with_error(elt, "Failed to parse indirect data stream: %s", buffer); goto cancel_wait; } *s++ = '\0'; data_host = buffer; data_port = atoi(s); str_to_sockaddr(data_host, &addr); SU_SET_PORT(&addr, data_port); str_sockaddr_r(&addr, strsockaddr, sizeof(strsockaddr)); } sock = socket(SU_GET_FAMILY(&addr), SOCK_STREAM, 0); g_debug("do_directtcp_connect making data connection to %s", strsockaddr); if (sock < 0) { xfer_cancel_with_error(elt, "socket(): %s", strerror(errno)); goto cancel_wait; } if (connect(sock, (struct sockaddr *)&addr, SS_LEN(&addr)) < 0) { xfer_cancel_with_error(elt, "connect(): %s", strerror(errno)); close(sock); goto cancel_wait; } g_debug("do_directtcp_connect: connected to %s, fd %d", strsockaddr, sock); return sock; cancel_wait: wait_until_xfer_cancelled(elt->xfer); return -1; }
static gboolean do_directtcp_listen( XferElement *elt, int *sockp, DirectTCPAddr **addrsp) { int sock; sockaddr_union data_addr; DirectTCPAddr *addrs; socklen_t len; struct addrinfo *res; struct addrinfo *res_addr; sockaddr_union *addr = NULL; if (resolve_hostname("localhost", 0, &res, NULL) != 0) { xfer_cancel_with_error(elt, "resolve_hostname(): %s", strerror(errno)); return FALSE; } for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) { if (res_addr->ai_family == AF_INET) { addr = (sockaddr_union *)res_addr->ai_addr; break; } } if (!addr) { addr = (sockaddr_union *)res->ai_addr; } sock = *sockp = socket(SU_GET_FAMILY(addr), SOCK_STREAM, 0); if (sock < 0) { xfer_cancel_with_error(elt, "socket(): %s", strerror(errno)); freeaddrinfo(res); return FALSE; } len = SS_LEN(addr); if (bind(sock, (struct sockaddr *)addr, len) != 0) { xfer_cancel_with_error(elt, "bind(): %s", strerror(errno)); freeaddrinfo(res); close(sock); *sockp = -1; return FALSE; } if (listen(sock, 1) < 0) { xfer_cancel_with_error(elt, "listen(): %s", strerror(errno)); freeaddrinfo(res); close(sock); *sockp = -1; return FALSE; } /* TODO: which addresses should this display? all ifaces? localhost? */ len = sizeof(data_addr); if (getsockname(sock, (struct sockaddr *)&data_addr, &len) < 0) error("getsockname(): %s", strerror(errno)); addrs = g_new0(DirectTCPAddr, 2); copy_sockaddr(&addrs[0], &data_addr); *addrsp = addrs; freeaddrinfo(res); return TRUE; }
int stream_server( int family, in_port_t *portp, size_t sendsize, size_t recvsize, int priv) { int server_socket, retries; socklen_t_equiv len; #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR) const int on = 1; int r; #endif sockaddr_union server; int save_errno; int *portrange; socklen_t_equiv socklen; int socket_family; *portp = USHRT_MAX; /* in case we error exit */ if (family == -1) { socket_family = AF_NATIVE; } else { socket_family = family; } g_debug("stream_server opening socket with family %d (requested family was %d)", socket_family, family); server_socket = socket(socket_family, SOCK_STREAM, 0); #ifdef WORKING_IPV6 /* if that address family actually isn't supported, just try AF_INET */ if (server_socket == -1 && errno == EAFNOSUPPORT) { g_debug("stream_server retrying socket with AF_INET"); socket_family = AF_INET; server_socket = socket(AF_INET, SOCK_STREAM, 0); } #endif if (server_socket == -1) { save_errno = errno; g_debug(_("stream_server: socket() failed: %s"), strerror(save_errno)); errno = save_errno; return -1; } if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) { aclose(server_socket); errno = EMFILE; /* out of range */ save_errno = errno; g_debug(_("stream_server: socket out of range: %d"), server_socket); errno = save_errno; return -1; } SU_INIT(&server, socket_family); SU_SET_INADDR_ANY(&server); #ifdef USE_REUSEADDR r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&on, (socklen_t_equiv)sizeof(on)); if (r < 0) { g_debug(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s"), strerror(errno)); } #endif try_socksize(server_socket, SO_SNDBUF, sendsize); try_socksize(server_socket, SO_RCVBUF, recvsize); /* * If a port range was specified, we try to get a port in that * range first. Next, we try to get a reserved port. If that * fails, we just go for any port. * * In all cases, not to use port that's assigned to other services. * * It is up to the caller to make sure we have the proper permissions * to get the desired port, and to make sure we return a port that * is within the range it requires. */ for (retries = 0; ; retries++) { if (priv) { portrange = getconf_intrange(CNF_RESERVED_TCP_PORT); } else { portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT); } if (portrange[0] != 0 && portrange[1] != 0) { if (bind_portrange(server_socket, &server, (in_port_t)portrange[0], (in_port_t)portrange[1], "tcp") == 0) goto out; g_debug(_("stream_server: Could not bind to port in range: %d - %d."), portrange[0], portrange[1]); } else { socklen = SS_LEN(&server); if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0) goto out; g_debug(_("stream_server: Could not bind to any port: %s"), strerror(errno)); } if (retries >= BIND_CYCLE_RETRIES) break; g_debug(_("stream_server: Retrying entire range after 10 second delay.")); sleep(15); } save_errno = errno; g_debug(_("stream_server: bind(in6addr_any) failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; out: listen(server_socket, 1); /* find out what port was actually used */ len = sizeof(server); if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) { save_errno = errno; g_debug(_("stream_server: getsockname() failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; } #ifdef SO_KEEPALIVE r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, (socklen_t_equiv)sizeof(on)); if(r == -1) { save_errno = errno; g_debug(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; } #endif *portp = SU_GET_PORT(&server); g_debug(_("stream_server: waiting for connection: %s"), str_sockaddr(&server)); return server_socket; }
/** Sends a request to a RADIUS server and waits for the reply * * @param rh a handle to parsed configuration * @param ctx if non-NULL it will contain the context of sent request; It must be released using rc_aaa_ctx_free(). * @param data a pointer to a SEND_DATA structure * @param msg must be an array of %PW_MAX_MSG_SIZE or NULL; will contain the concatenation of * any %PW_REPLY_MESSAGE received. * @param type must be %AUTH or %ACCT * @return OK_RC (0) on success, TIMEOUT_RC on timeout REJECT_RC on acess reject, or negative * on failure as return value. */ int rc_send_server_ctx(rc_handle * rh, RC_AAA_CTX ** ctx, SEND_DATA * data, char *msg, rc_type type) { int sockfd = -1; AUTH_HDR *auth, *recv_auth; char *server_name, *p; /* Name of server to query */ struct sockaddr_storage our_sockaddr; struct addrinfo *auth_addr = NULL; socklen_t salen; int result = 0; int total_length; int length, pos; int retry_max; const rc_sockets_override *sfuncs; unsigned discover_local_ip; size_t secretlen; char secret[MAX_SECRET_LENGTH + 1]; unsigned char vector[AUTH_VECTOR_LEN]; uint8_t recv_buffer[BUFFER_LEN]; uint8_t send_buffer[BUFFER_LEN]; uint8_t *attr; int retries; VALUE_PAIR *vp; struct pollfd pfd; double start_time, timeout; struct sockaddr_storage *ss_set = NULL; char *server_type = "auth"; server_name = data->server; if (server_name == NULL || server_name[0] == '\0') return ERROR_RC; if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE, 0)) && (vp->lvalue == PW_ADMINISTRATIVE)) { strcpy(secret, MGMT_POLL_SECRET); auth_addr = rc_getaddrinfo(server_name, type == AUTH ? PW_AI_AUTH : PW_AI_ACCT); if (auth_addr == NULL) return ERROR_RC; } else { if (data->secret != NULL) { strlcpy(secret, data->secret, MAX_SECRET_LENGTH); } /* else { */ if (rc_find_server_addr (rh, server_name, &auth_addr, secret, type) != 0) { rc_log(LOG_ERR, "rc_send_server: unable to find server: %s", server_name); return ERROR_RC; } /*} */ } sfuncs = &rh->so; if (sfuncs->static_secret) { /* any static secret set in sfuncs overrides the configured */ strlcpy(secret, sfuncs->static_secret, MAX_SECRET_LENGTH); } if (sfuncs->lock) { if (sfuncs->lock(sfuncs->ptr) != 0) { rc_log(LOG_ERR, "%s: lock error", __func__); return ERROR_RC; } } rc_own_bind_addr(rh, &our_sockaddr); discover_local_ip = 0; if (our_sockaddr.ss_family == AF_INET) { if (((struct sockaddr_in *)(&our_sockaddr))->sin_addr.s_addr == INADDR_ANY) { discover_local_ip = 1; } } DEBUG(LOG_ERR, "DEBUG: rc_send_server: creating socket to: %s", server_name); if (discover_local_ip) { result = rc_get_srcaddr(SA(&our_sockaddr), auth_addr->ai_addr); if (result != 0) { memset(secret, '\0', sizeof(secret)); rc_log(LOG_ERR, "rc_send_server: cannot figure our own address"); result = ERROR_RC; goto cleanup; } } if (sfuncs->get_fd) { sockfd = sfuncs->get_fd(sfuncs->ptr, SA(&our_sockaddr)); if (sockfd < 0) { memset(secret, '\0', sizeof(secret)); rc_log(LOG_ERR, "rc_send_server: socket: %s", strerror(errno)); result = ERROR_RC; goto cleanup; } } retry_max = data->retries; /* Max. numbers to try for reply */ retries = 0; /* Init retry cnt for blocking call */ if (data->svc_port) { if (our_sockaddr.ss_family == AF_INET) ((struct sockaddr_in *)auth_addr->ai_addr)->sin_port = htons((unsigned short)data->svc_port); else ((struct sockaddr_in6 *)auth_addr->ai_addr)->sin6_port = htons((unsigned short)data->svc_port); } /* * Fill in NAS-IP-Address (if needed) */ if (rh->nas_addr_set) { rc_avpair_remove(&(data->send_pairs), PW_NAS_IP_ADDRESS, 0); rc_avpair_remove(&(data->send_pairs), PW_NAS_IPV6_ADDRESS, 0); ss_set = &rh->nas_addr; } else if (rc_avpair_get(data->send_pairs, PW_NAS_IP_ADDRESS, 0) == NULL && rc_avpair_get(data->send_pairs, PW_NAS_IPV6_ADDRESS, 0) == NULL) { ss_set = &our_sockaddr; } if (ss_set) { if (ss_set->ss_family == AF_INET) { uint32_t ip; ip = *((uint32_t *) (&((struct sockaddr_in *)ss_set)-> sin_addr)); ip = ntohl(ip); rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IP_ADDRESS, &ip, 0, 0); } else { void *p; p = &((struct sockaddr_in6 *)ss_set)->sin6_addr; rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IPV6_ADDRESS, p, 16, 0); } } /* * Fill in NAS-Identifier (if needed) */ p = rc_conf_str(rh, "nas-identifier"); if (p != NULL) { rc_avpair_remove(&(data->send_pairs), PW_NAS_IDENTIFIER, 0); rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IDENTIFIER, p, -1, 0); } /* Build a request */ auth = (AUTH_HDR *) send_buffer; auth->code = data->code; auth->id = data->seq_nbr; if (data->code == PW_ACCOUNTING_REQUEST) { server_type = "acct"; total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons((unsigned short)total_length); memset((char *)auth->vector, 0, AUTH_VECTOR_LEN); secretlen = strlen(secret); memcpy((char *)auth + total_length, secret, secretlen); rc_md5_calc(vector, (unsigned char *)auth, total_length + secretlen); memcpy((char *)auth->vector, (char *)vector, AUTH_VECTOR_LEN); } else { rc_random_vector(vector); memcpy((char *)auth->vector, (char *)vector, AUTH_VECTOR_LEN); total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons((unsigned short)total_length); } if (radcli_debug) { char our_addr_txt[50] = ""; /* hold a text IP */ char auth_addr_txt[50] = ""; /* hold a text IP */ getnameinfo(SA(&our_sockaddr), SS_LEN(&our_sockaddr), NULL, 0, our_addr_txt, sizeof(our_addr_txt), NI_NUMERICHOST); getnameinfo(auth_addr->ai_addr, auth_addr->ai_addrlen, NULL, 0, auth_addr_txt, sizeof(auth_addr_txt), NI_NUMERICHOST); DEBUG(LOG_ERR, "DEBUG: timeout=%d retries=%d local %s : 0, remote %s : %u\n", data->timeout, retry_max, our_addr_txt, auth_addr_txt, data->svc_port); } for (;;) { do { result = sfuncs->sendto(sfuncs->ptr, sockfd, (char *)auth, (unsigned int)total_length, (int)0, SA(auth_addr->ai_addr), auth_addr->ai_addrlen); } while (result == -1 && errno == EINTR); if (result == -1) { rc_log(LOG_ERR, "%s: socket: %s", __FUNCTION__, strerror(errno)); result = ERROR_RC; goto cleanup; } pfd.fd = sockfd; pfd.events = POLLIN; pfd.revents = 0; start_time = rc_getmtime(); for (timeout = data->timeout; timeout > 0; timeout -= rc_getmtime() - start_time) { result = poll(&pfd, 1, timeout * 1000); if (result != -1 || errno != EINTR) break; } if (result == -1) { rc_log(LOG_ERR, "rc_send_server: poll: %s", strerror(errno)); memset(secret, '\0', sizeof(secret)); SCLOSE(sockfd); result = ERROR_RC; goto cleanup; } if (result == 1 && (pfd.revents & POLLIN) != 0) { salen = auth_addr->ai_addrlen; do { length = sfuncs->recvfrom(sfuncs->ptr, sockfd, (char *)recv_buffer, (int) sizeof(recv_buffer), (int)0, SA(auth_addr-> ai_addr), &salen); } while (length == -1 && errno == EINTR); if (length <= 0) { int e = errno; rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: %s", server_name, data->svc_port, strerror(e)); if (length == -1 && (e == EAGAIN || e == EINTR)) continue; SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } recv_auth = (AUTH_HDR *) recv_buffer; if (length < AUTH_HDR_LEN || length < ntohs(recv_auth->length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: reply is too short", server_name, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } result = rc_check_reply(recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr); if (result != BADRESPID_RC) { /* if a message that doesn't match our ID was received, then ignore * it, and try to receive more, until timeout. That is because in * DTLS the channel is shared, and we may receive duplicates or * out-of-order packets. */ break; } } /* * Timed out waiting for response. Retry "retry_max" times * before giving up. If retry_max = 0, don't retry at all. */ if (retries++ >= retry_max) { char radius_server_ip[128]; struct sockaddr_in *si = (struct sockaddr_in *)auth_addr->ai_addr; inet_ntop(auth_addr->ai_family, &si->sin_addr, radius_server_ip, sizeof(radius_server_ip)); rc_log(LOG_ERR, "rc_send_server: no reply from RADIUS %s server %s:%u", server_type, radius_server_ip, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = TIMEOUT_RC; goto cleanup; } } /* * If UDP is larger than RADIUS, shorten it to RADIUS. */ if (length > ntohs(recv_auth->length)) length = ntohs(recv_auth->length); /* * Verify that it's a valid RADIUS packet before doing ANYTHING with it. */ attr = recv_buffer + AUTH_HDR_LEN; while (attr < (recv_buffer + length)) { if (attr[0] == 0) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute zero is invalid", server_name, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } if (attr[1] < 2) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute length is too small", server_name, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } if ((attr + attr[1]) > (recv_buffer + length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute overflows the packet", server_name, data->svc_port); SCLOSE(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } attr += attr[1]; } length = ntohs(recv_auth->length) - AUTH_HDR_LEN; if (length > 0) { data->receive_pairs = rc_avpair_gen(rh, NULL, recv_auth->data, length, 0); } else { data->receive_pairs = NULL; } SCLOSE(sockfd); result = populate_ctx(ctx, secret, vector); if (result != OK_RC) { memset(secret, '\0', sizeof(secret)); goto cleanup; } memset(secret, '\0', sizeof(secret)); if (msg) { *msg = '\0'; pos = 0; vp = data->receive_pairs; while (vp) { if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE, 0))) { strappend(msg, PW_MAX_MSG_SIZE, &pos, vp->strvalue); strappend(msg, PW_MAX_MSG_SIZE, &pos, "\n"); vp = vp->next; } } } if ((recv_auth->code == PW_ACCESS_ACCEPT) || (recv_auth->code == PW_PASSWORD_ACK) || (recv_auth->code == PW_ACCOUNTING_RESPONSE)) { result = OK_RC; } else if ((recv_auth->code == PW_ACCESS_REJECT) || (recv_auth->code == PW_PASSWORD_REJECT)) { result = REJECT_RC; } else { rc_log(LOG_ERR, "rc_send_server: received RADIUS server response neither ACCEPT nor REJECT, invalid"); result = BADRESP_RC; } cleanup: if (auth_addr) freeaddrinfo(auth_addr); if (sfuncs->unlock) { if (sfuncs->unlock(sfuncs->ptr) != 0) { rc_log(LOG_ERR, "%s: unlock error", __func__); } } return result; }
/* * Bind to a port in the given range. Takes a begin,end pair of port numbers. * * Returns negative on error (EGAIN if all ports are in use). Returns 0 * on success. */ int bind_portrange( int s, sockaddr_union *addrp, in_port_t first_port, in_port_t last_port, char * proto) { in_port_t port; in_port_t cnt; socklen_t_equiv socklen; struct servent *servPort; const in_port_t num_ports = (in_port_t)(last_port - first_port + 1); int save_errno = EAGAIN; assert(first_port <= last_port); /* * We pick a different starting port based on our pid and the current * time to avoid always picking the same reserved port twice. */ port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port); /* * Scan through the range, trying all available ports that are either * not taken in /etc/services or registered for *amanda*. Wrap around * if we don't happen to start at the beginning. */ for (cnt = 0; cnt < num_ports; cnt++) { servPort = getservbyport((int)htons(port), proto); if ((servPort == NULL) || strstr(servPort->s_name, AMANDA_SERVICE_NAME)) { SU_SET_PORT(addrp, port); socklen = SS_LEN(addrp); if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) { if (servPort == NULL) { g_debug(_("bind_portrange2: Try port %d: Available - Success"), port); } else { g_debug(_("bind_portrange2: Try port %d: Owned by %s - Success."), port, servPort->s_name); } return 0; } if (errno != EAGAIN && errno != EBUSY) save_errno = errno; if (servPort == NULL) { g_debug(_("bind_portrange2: Try port %d: Available - %s"), port, strerror(errno)); } else { g_debug(_("bind_portrange2: Try port %d: Owned by %s - %s"), port, servPort->s_name, strerror(errno)); } } else { g_debug(_("bind_portrange2: Skip port %d: Owned by %s."), port, servPort->s_name); } if (++port > last_port) port = first_port; } g_debug(_("bind_portrange: all ports between %d and %d busy"), first_port, last_port); errno = save_errno; return -1; }
/* return >0: this is the connected socket */ int connect_port( sockaddr_union *addrp, in_port_t port, char * proto, sockaddr_union *svaddr, int nonblock) { int save_errno; struct servent * servPort; socklen_t_equiv len; socklen_t_equiv socklen; int s; servPort = getservbyport((int)htons(port), proto); if (servPort != NULL && !strstr(servPort->s_name, AMANDA_SERVICE_NAME)) { dbprintf(_("connect_port: Skip port %d: owned by %s.\n"), port, servPort->s_name); errno = EBUSY; return -1; } if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2; SU_SET_PORT(addrp, port); socklen = SS_LEN(addrp); if (bind(s, (struct sockaddr *)addrp, socklen) != 0) { save_errno = errno; aclose(s); if(servPort == NULL) { dbprintf(_("connect_port: Try port %d: available - %s\n"), port, strerror(errno)); } else { dbprintf(_("connect_port: Try port %d: owned by %s - %s\n"), port, servPort->s_name, strerror(errno)); } if (save_errno != EADDRINUSE) { errno = save_errno; return -2; } errno = save_errno; return -1; } if(servPort == NULL) { dbprintf(_("connect_port: Try port %d: available - Success\n"), port); } else { dbprintf(_("connect_port: Try port %d: owned by %s - Success\n"), port, servPort->s_name); } /* find out what port was actually used */ len = sizeof(*addrp); if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) { save_errno = errno; dbprintf(_("connect_port: getsockname() failed: %s\n"), strerror(save_errno)); aclose(s); errno = save_errno; return -1; } if (nonblock) { int r = fcntl(s, F_GETFL, 0); if (r < 0) { save_errno = errno; g_debug("Can't fcntl(F_GETFL): %s", strerror(errno)); aclose(s); errno = save_errno; return -1; } r = fcntl(s, F_SETFL, r|O_NONBLOCK); if (r < 0) { save_errno = errno; g_debug("Can't fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); errno = save_errno; aclose(s); return -1; } } if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) { save_errno = errno; dbprintf(_("connect_portrange: Connect from %s failed: %s\n"), str_sockaddr(addrp), strerror(save_errno)); dbprintf(_("connect_portrange: connect to %s failed: %s\n"), str_sockaddr(svaddr), strerror(save_errno)); aclose(s); errno = save_errno; if (save_errno == ECONNREFUSED || save_errno == EHOSTUNREACH || save_errno == ENETUNREACH || save_errno == ETIMEDOUT) { return -2 ; } return -1; } dbprintf(_("connected to %s\n"), str_sockaddr(svaddr)); dbprintf(_("our side is %s\n"), str_sockaddr(addrp)); return s; }
int dgram_send_addr( sockaddr_union *addr, dgram_t * dgram) { int s, rc; int socket_opened; int save_errno; int max_wait; int wait_count; #if defined(USE_REUSEADDR) const int on = 1; int r; #endif dbprintf(_("dgram_send_addr(addr=%p, dgram=%p)\n"), addr, dgram); dump_sockaddr(addr); dbprintf(_("dgram_send_addr: %p->socket = %d\n"), dgram, dgram->socket); if(dgram->socket != -1) { s = dgram->socket; socket_opened = 0; } else { int sndbufsize = MAX_DGRAM; g_debug("dgram_send_addr: setting up a socket with family %d", SU_GET_FAMILY(addr)); if((s = socket(SU_GET_FAMILY(addr), SOCK_DGRAM, 0)) == -1) { save_errno = errno; dbprintf(_("dgram_send_addr: socket() failed: %s\n"), strerror(save_errno)); errno = save_errno; return -1; } socket_opened = 1; #ifdef USE_REUSEADDR r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&on, (socklen_t_equiv)sizeof(on)); if (r < 0) { dbprintf(_("dgram_send_addr: setsockopt(SO_REUSEADDR) failed: %s\n"), strerror(errno)); } #endif /* try setting the buffer size (= maximum allowable UDP packet size) */ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbufsize, sizeof(sndbufsize)) < 0) { dbprintf("dgram_send_addr: could not set udp send buffer to %d: %s (ignored)\n", sndbufsize, strerror(errno)); } } if(s < 0 || s >= (int)FD_SETSIZE) { dbprintf(_("dgram_send_addr: socket out of range: %d\n"), s); errno = EMFILE; /* out of range */ rc = -1; } else { max_wait = 300 / 5; /* five minutes */ wait_count = 0; rc = 0; while(sendto(s, dgram->data, dgram->len, 0, (struct sockaddr *)addr, SS_LEN(addr)) == -1) { #ifdef ECONNREFUSED if(errno == ECONNREFUSED && wait_count++ < max_wait) { sleep(5); dbprintf(_("dgram_send_addr: sendto(%s): retry %d after ECONNREFUSED\n"), str_sockaddr(addr), wait_count); continue; } #endif #ifdef EAGAIN if(errno == EAGAIN && wait_count++ < max_wait) { sleep(5); dbprintf(_("dgram_send_addr: sendto(%s): retry %d after EAGAIN\n"), str_sockaddr(addr), wait_count); continue; } #endif save_errno = errno; dbprintf(_("dgram_send_addr: sendto(%s) failed: %s \n"), str_sockaddr(addr), strerror(save_errno)); errno = save_errno; rc = -1; break; } } if(socket_opened) { save_errno = errno; if(close(s) == -1) { dbprintf(_("dgram_send_addr: close(%s): failed: %s\n"), str_sockaddr(addr), strerror(errno)); /* * Calling function should not care that the close failed. * It does care about the send results though. */ } errno = save_errno; } return rc; }
/** Sends a request to a RADIUS server and waits for the reply * * @param rh a handle to parsed configuration * @param data a pointer to a #SEND_DATA structure * @param msg must be an array of %PW_MAX_MSG_SIZE or %NULL; will contain the concatenation of * any %PW_REPLY_MESSAGE received. * @param flags must be %AUTH or %ACCT * @return %OK_RC (0) on success, %TIMEOUT_RC on timeout %REJECT_RC on acess reject, or negative * on failure as return value. */ int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg, unsigned flags) { int sockfd; AUTH_HDR *auth, *recv_auth; char *server_name; /* Name of server to query */ struct sockaddr_storage our_sockaddr; struct addrinfo *auth_addr = NULL; socklen_t salen; int result = 0; int total_length; int length, pos; int retry_max; unsigned discover_local_ip; size_t secretlen; char secret[MAX_SECRET_LENGTH + 1]; unsigned char vector[AUTH_VECTOR_LEN]; uint8_t recv_buffer[BUFFER_LEN]; uint8_t send_buffer[BUFFER_LEN]; char our_addr_txt[50]; /* hold a text IP */ char auth_addr_txt[50]; /* hold a text IP */ uint8_t *attr; int retries; VALUE_PAIR *vp; struct pollfd pfd; double start_time, timeout; server_name = data->server; if (server_name == NULL || server_name[0] == '\0') return ERROR_RC; if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE, 0)) && \ (vp->lvalue == PW_ADMINISTRATIVE)) { strcpy(secret, MGMT_POLL_SECRET); auth_addr = rc_getaddrinfo(server_name, flags==AUTH?PW_AI_AUTH:PW_AI_ACCT); if (auth_addr == NULL) return ERROR_RC; } else { if(data->secret != NULL) { strlcpy(secret, data->secret, MAX_SECRET_LENGTH); } /* else { */ if (rc_find_server_addr (rh, server_name, &auth_addr, secret, flags) != 0) { rc_log(LOG_ERR, "rc_send_server: unable to find server: %s", server_name); return ERROR_RC; } /*}*/ } rc_own_bind_addr(rh, &our_sockaddr); discover_local_ip = 0; if (our_sockaddr.ss_family == AF_INET) { if (((struct sockaddr_in*)(&our_sockaddr))->sin_addr.s_addr == INADDR_ANY) { discover_local_ip = 1; } } DEBUG(LOG_ERR, "DEBUG: rc_send_server: creating socket to: %s", server_name); if (discover_local_ip) { result = rc_get_srcaddr(SA(&our_sockaddr), auth_addr->ai_addr); if (result != 0) { memset (secret, '\0', sizeof (secret)); rc_log(LOG_ERR, "rc_send_server: cannot figure our own address"); result = ERROR_RC; goto cleanup; } } sockfd = socket (our_sockaddr.ss_family, SOCK_DGRAM, 0); if (sockfd < 0) { memset (secret, '\0', sizeof (secret)); rc_log(LOG_ERR, "rc_send_server: socket: %s", strerror(errno)); result = ERROR_RC; goto cleanup; } if (our_sockaddr.ss_family == AF_INET) ((struct sockaddr_in*)&our_sockaddr)->sin_port = 0; else ((struct sockaddr_in6*)&our_sockaddr)->sin6_port = 0; if (bind(sockfd, SA(&our_sockaddr), SS_LEN(&our_sockaddr)) < 0) { close (sockfd); memset (secret, '\0', sizeof (secret)); rc_log(LOG_ERR, "rc_send_server: bind: %s: %s", server_name, strerror(errno)); result = ERROR_RC; goto cleanup; } retry_max = data->retries; /* Max. numbers to try for reply */ retries = 0; /* Init retry cnt for blocking call */ if (data->svc_port) { if (our_sockaddr.ss_family == AF_INET) ((struct sockaddr_in*)auth_addr->ai_addr)->sin_port = htons ((unsigned short) data->svc_port); else ((struct sockaddr_in6*)auth_addr->ai_addr)->sin6_port = htons ((unsigned short) data->svc_port); } /* * Fill in NAS-IP-Address (if needed) */ if (rc_avpair_get(data->send_pairs, PW_NAS_IP_ADDRESS, 0) == NULL && rc_avpair_get(data->send_pairs, PW_NAS_IPV6_ADDRESS, 0) == NULL) { if (our_sockaddr.ss_family == AF_INET) { uint32_t ip; ip = *((uint32_t*)(&((struct sockaddr_in*)&our_sockaddr)->sin_addr)); ip = ntohl(ip); rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IP_ADDRESS, &ip, 0, 0); } else { void *p; p = &((struct sockaddr_in6*)&our_sockaddr)->sin6_addr; rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IPV6_ADDRESS, p, 0, 0); } } /* Build a request */ auth = (AUTH_HDR *) send_buffer; auth->code = data->code; auth->id = data->seq_nbr; if (data->code == PW_ACCOUNTING_REQUEST) { total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons ((unsigned short) total_length); memset((char *) auth->vector, 0, AUTH_VECTOR_LEN); secretlen = strlen (secret); memcpy ((char *) auth + total_length, secret, secretlen); rc_md5_calc (vector, (unsigned char *) auth, total_length + secretlen); memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); } else { rc_random_vector (vector); memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN); total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN; auth->length = htons ((unsigned short) total_length); } getnameinfo(SA(&our_sockaddr), SS_LEN(&our_sockaddr), NULL, 0, our_addr_txt, sizeof(our_addr_txt), NI_NUMERICHOST); getnameinfo(auth_addr->ai_addr, auth_addr->ai_addrlen, NULL, 0, auth_addr_txt, sizeof(auth_addr_txt), NI_NUMERICHOST); DEBUG(LOG_ERR, "DEBUG: local %s : 0, remote %s : %u\n", our_addr_txt, auth_addr_txt, data->svc_port); for (;;) { do { result = sendto (sockfd, (char *) auth, (unsigned int)total_length, (int) 0, SA(auth_addr->ai_addr), auth_addr->ai_addrlen); } while (result == -1 && errno == EINTR); if (result == -1) { rc_log(LOG_ERR, "%s: socket: %s", __FUNCTION__, strerror(errno)); } pfd.fd = sockfd; pfd.events = POLLIN; pfd.revents = 0; if(data->timeout_ms > 0){ start_time = rc_getctime_ms(); for (timeout = data->timeout_ms; timeout > 0;timeout -= rc_getctime_ms() - start_time) { result = poll(&pfd, 1, timeout ); if (result != -1 || errno != EINTR) break; } }else{ start_time = rc_getctime(); for (timeout = data->timeout; timeout > 0;timeout -= rc_getctime() - start_time) { result = poll(&pfd, 1, timeout* 1000); if (result != -1 || errno != EINTR) break; } } if (result == -1) { rc_log(LOG_ERR, "rc_send_server: poll: %s", strerror(errno)); memset (secret, '\0', sizeof (secret)); close (sockfd); result = ERROR_RC; goto cleanup; } if (result == 1 && (pfd.revents & POLLIN) != 0) break; /* * Timed out waiting for response. Retry "retry_max" times * before giving up. If retry_max = 0, don't retry at all. */ if (retries++ >= retry_max) { rc_log(LOG_ERR, "rc_send_server: no reply from RADIUS server %s:%u", auth_addr_txt, data->svc_port); close (sockfd); memset (secret, '\0', sizeof (secret)); result = TIMEOUT_RC; goto cleanup; } } salen = auth_addr->ai_addrlen; do { length = recvfrom (sockfd, (char *) recv_buffer, (int) sizeof (recv_buffer), (int) 0, SA(auth_addr->ai_addr), &salen); } while(length == -1 && errno == EINTR); if (length <= 0) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: %s", server_name,\ data->svc_port, strerror(errno)); close (sockfd); memset (secret, '\0', sizeof (secret)); result = ERROR_RC; goto cleanup; } recv_auth = (AUTH_HDR *)recv_buffer; if (length < AUTH_HDR_LEN || length < ntohs(recv_auth->length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: reply is too short", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); result = ERROR_RC; goto cleanup; } /* * If UDP is larger than RADIUS, shorten it to RADIUS. */ if (length > ntohs(recv_auth->length)) length = ntohs(recv_auth->length); /* * Verify that it's a valid RADIUS packet before doing ANYTHING with it. */ attr = recv_buffer + AUTH_HDR_LEN; while (attr < (recv_buffer + length)) { if (attr[0] == 0) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute zero is invalid", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); return ERROR_RC; } if (attr[1] < 2) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute length is too small", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); return ERROR_RC; } if ((attr + attr[1]) > (recv_buffer + length)) { rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute overflows the packet", server_name, data->svc_port); close(sockfd); memset(secret, '\0', sizeof(secret)); return ERROR_RC; } attr += attr[1]; } result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr); length = ntohs(recv_auth->length) - AUTH_HDR_LEN; if (length > 0) { data->receive_pairs = rc_avpair_gen(rh, NULL, recv_auth->data, length, 0); } else { data->receive_pairs = NULL; } close (sockfd); memset (secret, '\0', sizeof (secret)); if (result != OK_RC) { goto cleanup; } if (msg) { *msg = '\0'; pos = 0; vp = data->receive_pairs; while (vp) { if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE, 0))) { strappend(msg, PW_MAX_MSG_SIZE, &pos, vp->strvalue); strappend(msg, PW_MAX_MSG_SIZE, &pos, "\n"); vp = vp->next; } } } if ((recv_auth->code == PW_ACCESS_ACCEPT) || (recv_auth->code == PW_PASSWORD_ACK) || (recv_auth->code == PW_ACCOUNTING_RESPONSE)) { result = OK_RC; } else if ((recv_auth->code == PW_ACCESS_REJECT) || (recv_auth->code == PW_PASSWORD_REJECT)) { result = REJECT_RC; } else { result = BADRESP_RC; } cleanup: if (auth_addr) freeaddrinfo(auth_addr); return result; }