Example #1
0
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);
}
Example #2
0
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);
}
Example #3
0
/** 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;
}
Example #4
0
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;
}
Example #5
0
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;
    }
}
Example #6
0
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);
}
Example #7
0
/* 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);
}
Example #8
0
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;
}
Example #9
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;
}
Example #10
0
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;
}
Example #11
0
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;
}
Example #12
0
/** 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;
}
Example #13
0
/*
 * 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;
}
Example #14
0
/* 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;
}
Example #15
0
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;
}
Example #16
0
/** 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;
}