/* * pptp.c * * Written by Archie Cobbs * Copyright (c) 1998-1999 Whistle Communications, Inc. All rights reserved. * See ``COPYRIGHT.whistle'' */ #include "ppp.h" #include "phys.h" #include "mbuf.h" #include "ngfunc.h" #include "pptp.h" #include "pptp_ctrl.h" #include "log.h" #include #ifdef __DragonFly__ #include #include #include #else #include #include #include #endif #include /* * DEFINITIONS */ #define PPTP_MRU PPTP_MTU #define PPTP_MAX_ERRORS 10 #define PPTP_REOPEN_PAUSE 5 #define MAX_IOVEC 32 #define PPTP_CALL_MIN_BPS 56000 #define PPTP_CALL_MAX_BPS 64000 struct pptpinfo { struct { struct u_addr self_addr; /* self IP address */ struct u_range peer_addr; /* Peer IP addresses allowed */ in_port_t self_port; /* self port */ in_port_t peer_port; /* Peer port required (or zero) */ struct optinfo options; char callingnum[64]; /* PPTP phone number to use */ char callednum[64]; /* PPTP phone number to use */ } conf; void *listener; /* Listener pointer */ struct u_addr self_addr; /* Current self IP address */ struct u_addr peer_addr; /* Current peer IP address */ in_port_t peer_port; /* Current peer port */ u_char originate; /* Call originated locally */ u_char outcall; /* Call is outgoing vs. incoming */ u_char sync; /* Call is sync vs. async */ struct pptpctrlinfo cinfo; ng_ID_t node_id; char callingnum[64]; /* PPTP phone number to use */ char callednum[64]; /* PPTP phone number to use */ }; typedef struct pptpinfo *PptpInfo; /* Set menu options */ enum { SET_SELFADDR, SET_PEERADDR, SET_CALLINGNUM, SET_CALLEDNUM, SET_ENABLE, SET_DISABLE, }; /* Binary options */ enum { PPTP_CONF_ORIGINATE, /* allow originating connections to peer */ PPTP_CONF_INCOMING, /* allow accepting connections from peer */ PPTP_CONF_OUTCALL, /* when originating, calls are "outgoing" */ PPTP_CONF_DELAYED_ACK, /* enable delayed receive ack algorithm */ PPTP_CONF_ALWAYS_ACK, /* include ack with all outgoing data packets */ #if NGM_PPTPGRE_COOKIE >= 1082548365 PPTP_CONF_WINDOWING, /* control (stupid) windowing algorithm */ #endif }; /* * INTERNAL FUNCTIONS */ static int PptpInit(PhysInfo p); static void PptpOpen(PhysInfo p); static void PptpClose(PhysInfo p); static void PptpShutdown(PhysInfo p); static void PptpStat(Context ctx); static int PptpOriginated(PhysInfo p); static int PptpIsSync(PhysInfo p); static int PptpSetAccm(PhysInfo p, u_int32_t xmit, u_int32_t recv); static int PptpSetCallingNum(PhysInfo p, void *buf); static int PptpSetCalledNum(PhysInfo p, void *buf); static int PptpPeerAddr(PhysInfo p, void *buf, int buf_len); static int PptpPeerPort(PhysInfo p, void *buf, int buf_len); static int PptpCallingNum(PhysInfo p, void *buf, int buf_len); static int PptpCalledNum(PhysInfo p, void *buf, int buf_len); static void PptpInitCtrl(void); static int PptpOriginate(PhysInfo p); static void PptpDoClose(PhysInfo p); static void PptpKillNode(PhysInfo p); static void PptpResult(void *cookie, const char *errmsg, int frameType); static void PptpSetLinkInfo(void *cookie, u_int32_t sa, u_int32_t ra); static void PptpCancel(void *cookie); static int PptpHookUp(PhysInfo p); static struct pptplinkinfo PptpIncoming(struct pptpctrlinfo *cinfo, struct u_addr *self, struct u_addr *peer, in_port_t port, int bearType, const char *callingNum, const char *calledNum, const char *subAddress); static struct pptplinkinfo PptpOutgoing(struct pptpctrlinfo *cinfo, struct u_addr *self, struct u_addr *peer, in_port_t port, int bearType, int frameType, int minBps, int maxBps, const char *calledNum, const char *subAddress); static struct pptplinkinfo PptpPeerCall(struct pptpctrlinfo *cinfo, struct u_addr *self, struct u_addr *peer, in_port_t port, int incoming, const char *callingNum, const char *calledNum, const char *subAddress); static int PptpSetCommand(Context ctx, int ac, char *av[], void *arg); /* * GLOBAL VARIABLES */ const struct phystype gPptpPhysType = { .name = "pptp", .minReopenDelay = PPTP_REOPEN_PAUSE, .mtu = PPTP_MTU, .mru = PPTP_MRU, .init = PptpInit, .open = PptpOpen, .close = PptpClose, .shutdown = PptpShutdown, .showstat = PptpStat, .originate = PptpOriginated, .issync = PptpIsSync, .setaccm = PptpSetAccm, .setcallingnum = PptpSetCallingNum, .setcallednum = PptpSetCalledNum, .peeraddr = PptpPeerAddr, .peerport = PptpPeerPort, .callingnum = PptpCallingNum, .callednum = PptpCalledNum, }; const struct cmdtab PptpSetCmds[] = { { "self ip [port]", "Set local IP address", PptpSetCommand, NULL, (void *) SET_SELFADDR }, { "peer ip [port]", "Set remote IP address", PptpSetCommand, NULL, (void *) SET_PEERADDR }, { "callingnum number", "Set calling PPTP telephone number", PptpSetCommand, NULL, (void *) SET_CALLINGNUM }, { "callednum number", "Set called PPTP telephone number", PptpSetCommand, NULL, (void *) SET_CALLEDNUM }, { "enable [opt ...]", "Enable option", PptpSetCommand, NULL, (void *) SET_ENABLE }, { "disable [opt ...]", "Disable option", PptpSetCommand, NULL, (void *) SET_DISABLE }, { NULL }, }; /* * INTERNAL VARIABLES */ static u_char gInitialized; static struct confinfo gConfList[] = { { 0, PPTP_CONF_ORIGINATE, "originate" }, { 0, PPTP_CONF_INCOMING, "incoming" }, { 0, PPTP_CONF_OUTCALL, "outcall" }, { 0, PPTP_CONF_DELAYED_ACK, "delayed-ack" }, { 0, PPTP_CONF_ALWAYS_ACK, "always-ack" }, #if NGM_PPTPGRE_COOKIE >= 1082548365 { 0, PPTP_CONF_WINDOWING, "windowing" }, #endif { 0, 0, NULL }, }; /* * PptpInit() */ static int PptpInit(PhysInfo p) { PptpInfo pptp; /* Initialize this link */ pptp = (PptpInfo) (p->info = Malloc(MB_PHYS, sizeof(*pptp))); pptp->conf.self_addr.family = AF_INET; Enable(&pptp->conf.options, PPTP_CONF_OUTCALL); Enable(&pptp->conf.options, PPTP_CONF_DELAYED_ACK); #if NGM_PPTPGRE_COOKIE >= 1082548365 Enable(&pptp->conf.options, PPTP_CONF_WINDOWING); #endif /* Initialize first time */ if (!gInitialized) { PptpInitCtrl(); } return(0); } /* * PptpOpen() */ static void PptpOpen(PhysInfo p) { PptpInfo const pptp = (PptpInfo) p->info; /* Check state */ switch (p->state) { case PHYS_STATE_DOWN: if (!Enabled(&pptp->conf.options, PPTP_CONF_ORIGINATE)) { Log(LG_ERR, ("[%s] pptp originate option is not enabled", p->name)); PhysDown(p, STR_DEV_NOT_READY, NULL); return; } if (PptpOriginate(p) < 0) { Log(LG_PHYS, ("[%s] PPTP call failed", p->name)); PhysDown(p, STR_ERROR, NULL); return; } p->state = PHYS_STATE_CONNECTING; break; case PHYS_STATE_CONNECTING: if (pptp->originate) /* our call to peer is already in progress */ break; if (pptp->outcall) { /* Hook up nodes */ Log(LG_PHYS, ("[%s] PPTP: attaching to peer's outgoing call", p->name)); if (PptpHookUp(p) < 0) { PptpDoClose(p); /* We should not set state=DOWN as PptpResult() will be called once more */ break; } p->state = PHYS_STATE_UP; PhysUp(p); (*pptp->cinfo.answer)(pptp->cinfo.cookie, PPTP_OCR_RESL_OK, 0, 0, 64000 /*XXX*/ ); return; } return; /* wait for peer's incoming pptp call to complete */ case PHYS_STATE_UP: PhysUp(p); return; default: assert(0); } } /* * PptpOriginate() * * Initiate an "incoming" or an "outgoing" call to the remote site */ static int PptpOriginate(PhysInfo p) { PptpInfo const pptp = (PptpInfo) p->info; struct pptpctrlinfo cinfo; struct pptplinkinfo linfo; const u_short port = pptp->conf.peer_port ? pptp->conf.peer_port : PPTP_PORT; pptp->originate = TRUE; pptp->outcall = Enabled(&pptp->conf.options, PPTP_CONF_OUTCALL); memset(&linfo, 0, sizeof(linfo)); linfo.cookie = p; linfo.result = PptpResult; linfo.setLinkInfo = PptpSetLinkInfo; linfo.cancel = PptpCancel; strlcpy(pptp->callingnum, pptp->conf.callingnum, sizeof(pptp->callingnum)); strlcpy(pptp->callednum, pptp->conf.callednum, sizeof(pptp->callednum)); if (!pptp->outcall) { int frameType = PPTP_FRAMECAP_SYNC; if (p->rep && !RepIsSync(p)) frameType = PPTP_FRAMECAP_ASYNC; cinfo = PptpCtrlInCall(linfo, &pptp->conf.self_addr, &pptp->conf.peer_addr.addr, port, PPTP_BEARCAP_ANY, frameType, PPTP_CALL_MIN_BPS, PPTP_CALL_MAX_BPS, pptp->callingnum, pptp->callednum, ""); } else { cinfo = PptpCtrlOutCall(linfo, &pptp->conf.self_addr, &pptp->conf.peer_addr.addr, port, PPTP_BEARCAP_ANY, PPTP_FRAMECAP_ANY, PPTP_CALL_MIN_BPS, PPTP_CALL_MAX_BPS, pptp->callednum, ""); } if (cinfo.cookie == NULL) return(-1); pptp->self_addr = pptp->conf.self_addr; pptp->peer_addr = pptp->conf.peer_addr.addr; pptp->peer_port = port; pptp->cinfo = cinfo; return(0); } /* * PptpClose() */ static void PptpClose(PhysInfo p) { PptpDoClose(p); } /* * PptpShutdown() */ static void PptpShutdown(PhysInfo p) { PptpInfo const pptp = (PptpInfo) p->info; if (pptp->listener) { PptpCtrlUnListen(pptp->listener); pptp->listener = NULL; } PptpKillNode(p); } /* * PptpDoClose() */ static void PptpDoClose(PhysInfo p) { PptpInfo const pptp = (PptpInfo) p->info; if (p->state != PHYS_STATE_DOWN) { /* avoid double close */ (*pptp->cinfo.close)(pptp->cinfo.cookie, PPTP_CDN_RESL_ADMIN, 0, 0); PptpKillNode(p); } } /* * PptpKillNode() */ static void PptpKillNode(PhysInfo p) { PptpInfo const pptp = (PptpInfo) p->info; char path[NG_PATHLEN + 1]; int csock = -1; if (pptp->node_id == 0) return; /* Get a temporary netgraph socket node */ if (NgMkSockNode(NULL, &csock, NULL) == -1) { Log(LG_ERR, ("PPTP: NgMkSockNode: %s", strerror(errno))); return; } /* Disconnect session hook. */ snprintf(path, sizeof(path), "[%lx]:", (u_long)pptp->node_id); NgFuncShutdownNode(csock, p->name, path); close(csock); pptp->node_id = 0; } /* * PptpOriginated() */ static int PptpOriginated(PhysInfo p) { PptpInfo const pptp = (PptpInfo) p->info; return(pptp->originate ? LINK_ORIGINATE_LOCAL : LINK_ORIGINATE_REMOTE); } /* * PptpIsSync() */ static int PptpIsSync(PhysInfo p) { PptpInfo const pptp = (PptpInfo) p->info; return (pptp->sync); } static int PptpSetAccm(PhysInfo p, u_int32_t xmit, u_int32_t recv) { PptpInfo const pptp = (PptpInfo) p->info; if (!pptp->cinfo.close || !pptp->cinfo.cookie) return (-1); (*pptp->cinfo.setLinkInfo)(pptp->cinfo.cookie, xmit, recv); return (0); } static int PptpSetCallingNum(PhysInfo p, void *buf) { PptpInfo const pptp = (PptpInfo) p->info; strlcpy(pptp->conf.callingnum, buf, sizeof(pptp->conf.callingnum)); return(0); } static int PptpSetCalledNum(PhysInfo p, void *buf) { PptpInfo const pptp = (PptpInfo) p->info; strlcpy(pptp->conf.callednum, buf, sizeof(pptp->conf.callednum)); return(0); } static int PptpPeerAddr(PhysInfo p, void *buf, int buf_len) { PptpInfo const pptp = (PptpInfo) p->info; if (u_addrtoa(&pptp->peer_addr, buf, buf_len)) return(0); else return(-1); } static int PptpPeerPort(PhysInfo p, void *buf, int buf_len) { PptpInfo const pptp = (PptpInfo) p->info; if (snprintf(buf, buf_len, "%d", pptp->peer_port)) return(0); else return(-1); } static int PptpCallingNum(PhysInfo p, void *buf, int buf_len) { PptpInfo const pptp = (PptpInfo) p->info; strlcpy((char*)buf, pptp->callingnum, buf_len); return(0); } static int PptpCalledNum(PhysInfo p, void *buf, int buf_len) { PptpInfo const pptp = (PptpInfo) p->info; strlcpy((char*)buf, pptp->callednum, buf_len); return(0); } /* * PptpStat() */ void PptpStat(Context ctx) { PptpInfo const pptp = (PptpInfo) ctx->phys->info; char buf[32]; Printf("PPTP configuration:\r\n"); Printf("\tSelf addr : %s", u_addrtoa(&pptp->conf.self_addr, buf, sizeof(buf))); if (pptp->conf.self_port) Printf(", port %u", pptp->conf.self_port); Printf("\r\n"); Printf("\tPeer range : %s", u_rangetoa(&pptp->conf.peer_addr, buf, sizeof(buf))); if (pptp->conf.peer_port) Printf(", port %u", pptp->conf.peer_port); Printf("\r\n"); Printf("\tCalling number: %s\r\n", pptp->conf.callingnum); Printf("\tCalled number: %s\r\n", pptp->conf.callednum); Printf("PPTP options:\r\n"); OptStat(ctx, &pptp->conf.options, gConfList); Printf("PPTP status:\r\n"); Printf("\tState : %s\r\n", gPhysStateNames[ctx->phys->state]); if (ctx->phys->state != PHYS_STATE_DOWN) { Printf("\tIncoming : %s\r\n", (pptp->originate?"NO":"YES")); Printf("\tCurrent self : %s\r\n", u_addrtoa(&pptp->self_addr, buf, sizeof(buf))); Printf("\tCurrent peer : %s, port %u\r\n", u_addrtoa(&pptp->peer_addr, buf, sizeof(buf)), pptp->peer_port); Printf("\tFraming : %s\r\n", (pptp->sync?"Sync":"Async")); Printf("\tCalling number: %s\r\n", pptp->callingnum); Printf("\tCalled number: %s\r\n", pptp->callednum); } } /* * PptpInitCtrl() */ static void PptpInitCtrl(void) { if (PptpCtrlInit(PptpIncoming, PptpOutgoing) < 0) { Log(LG_ERR, ("PPTP ctrl init failed")); return; } gInitialized = TRUE; } /* * PptpResult() * * The control code calls this function to report a PPTP link * being connected, disconnected, or failing to connect. */ static void PptpResult(void *cookie, const char *errmsg, int frameType) { PptpInfo pptp; PhysInfo p; /* It this fake call? */ if (!cookie) return; p = (PhysInfo)cookie; pptp = (PptpInfo) p->info; switch (p->state) { case PHYS_STATE_CONNECTING: if (!errmsg) { /* Hook up nodes */ Log(LG_PHYS, ("[%s] PPTP call successful", p->name)); if (PptpHookUp(p) < 0) { PptpDoClose(p); /* We should not set state=DOWN as PptpResult() will be called once more */ break; } if (pptp->originate && !pptp->outcall) (*pptp->cinfo.connected)(pptp->cinfo.cookie, 64000 /*XXX*/ ); /* OK */ p->state = PHYS_STATE_UP; pptp->sync = (frameType&PPTP_FRAMECAP_ASYNC)?0:1; PhysUp(p); } else { Log(LG_PHYS, ("[%s] PPTP call failed", p->name)); PhysDown(p, STR_CON_FAILED, "%s", errmsg); p->state = PHYS_STATE_DOWN; u_addrclear(&pptp->self_addr); u_addrclear(&pptp->peer_addr); pptp->peer_port = 0; pptp->callingnum[0]=0; pptp->callednum[0]=0; } break; case PHYS_STATE_UP: assert(errmsg); Log(LG_PHYS, ("[%s] PPTP call terminated", p->name)); PptpDoClose(p); PhysDown(p, STR_DROPPED, NULL); p->state = PHYS_STATE_DOWN; u_addrclear(&pptp->self_addr); u_addrclear(&pptp->peer_addr); pptp->peer_port = 0; pptp->callingnum[0]=0; pptp->callednum[0]=0; break; case PHYS_STATE_DOWN: return; default: assert(0); } } /* * PptpSetLinkInfo() * * Received LinkInfo from peer; */ void PptpSetLinkInfo(void *cookie, u_int32_t sa, u_int32_t ra) { PhysInfo p; /* It this fake call? */ if (!cookie) return; p = (PhysInfo)cookie; if (p->rep != NULL) { RepSetAccm(p, sa, ra); } }; /* * PptpHookUp() * * Connect the PPTP/GRE node to the PPP node */ static int PptpHookUp(PhysInfo p) { const PptpInfo pi = (PptpInfo)p->info; char ksockpath[NG_PATHLEN+1]; char pptppath[NG_PATHLEN+1]; struct ngm_mkpeer mkp; struct ng_pptpgre_conf gc; struct sockaddr_storage self_addr, peer_addr; struct u_addr u_self_addr, u_peer_addr; union { u_char buf[sizeof(struct ng_ksocket_sockopt) + sizeof(int)]; struct ng_ksocket_sockopt ksso; } u; struct ng_ksocket_sockopt *const ksso = &u.ksso; int csock = -1; char path[NG_PATHLEN + 1]; char hook[NG_HOOKLEN + 1]; union { u_char buf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)]; struct ng_mesg reply; } repbuf; struct ng_mesg *const reply = &repbuf.reply; struct nodeinfo *ninfo = (struct nodeinfo *)&reply->data; /* Get session info */ memset(&gc, 0, sizeof(gc)); PptpCtrlGetSessionInfo(&pi->cinfo, &u_self_addr, &u_peer_addr, &gc.cid, &gc.peerCid, &gc.recvWin, &gc.peerPpd); u_addrtosockaddr(&u_self_addr, 0, &self_addr); u_addrtosockaddr(&u_peer_addr, 0, &peer_addr); if (!PhysGetUpperHook(p, path, hook)) { Log(LG_PHYS, ("[%s] PPTP: can't get upper hook", p->name)); return(-1); } /* Get a temporary netgraph socket node */ if (NgMkSockNode(NULL, &csock, NULL) == -1) { Log(LG_ERR, ("PPTP: NgMkSockNode: %s", strerror(errno))); return(-1); } /* Attach PPTP/GRE node to PPP node */ snprintf(mkp.type, sizeof(mkp.type), "%s", NG_PPTPGRE_NODE_TYPE); snprintf(mkp.ourhook, sizeof(mkp.ourhook), "%s", hook); snprintf(mkp.peerhook, sizeof(mkp.peerhook), "%s", NG_PPTPGRE_HOOK_UPPER); if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof(mkp)) < 0) { Log(LG_ERR, ("[%s] PPTP: can't attach %s node: %s", p->name, NG_PPTPGRE_NODE_TYPE, strerror(errno))); close(csock); return(-1); } snprintf(pptppath, sizeof(pptppath), "%s.%s", path, hook); /* Get pptpgre node ID */ if (NgSendMsg(csock, pptppath, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) != -1) { if (NgRecvMsg(csock, reply, sizeof(repbuf), NULL) != -1) { pi->node_id = ninfo->id; } } /* Attach ksocket node to PPTP/GRE node */ snprintf(mkp.type, sizeof(mkp.type), "%s", NG_KSOCKET_NODE_TYPE); snprintf(mkp.ourhook, sizeof(mkp.ourhook), "%s", NG_PPTPGRE_HOOK_LOWER); if (u_self_addr.family==AF_INET6) { //ng_ksocket doesn't support inet6 name snprintf(mkp.peerhook, sizeof(mkp.peerhook), "%d/%d/%d", PF_INET6, SOCK_RAW, IPPROTO_GRE); } else { snprintf(mkp.peerhook, sizeof(mkp.peerhook), "inet/raw/gre"); } if (NgSendMsg(csock, pptppath, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof(mkp)) < 0) { Log(LG_ERR, ("[%s] PPTP: can't attach %s node: %s", p->name, NG_KSOCKET_NODE_TYPE, strerror(errno))); close(csock); return(-1); } snprintf(ksockpath, sizeof(ksockpath), "%s.%s", pptppath, NG_PPTPGRE_HOOK_LOWER); /* increase recvspace to avoid packet loss due to very small GRE recv buffer. */ ksso->level=SOL_SOCKET; ksso->name=SO_RCVBUF; ((int *)(ksso->value))[0]=48*1024; if (NgSendMsg(csock, ksockpath, NGM_KSOCKET_COOKIE, NGM_KSOCKET_SETOPT, &u, sizeof(u)) < 0) { Log(LG_ERR, ("[%s] PPTP: can't setsockopt %s node: %s", p->name, NG_KSOCKET_NODE_TYPE, strerror(errno))); } /* Bind ksocket socket to local IP address */ if (NgSendMsg(csock, ksockpath, NGM_KSOCKET_COOKIE, NGM_KSOCKET_BIND, &self_addr, self_addr.ss_len) < 0) { Log(LG_ERR, ("[%s] PPTP: can't bind() %s node: %s", p->name, NG_KSOCKET_NODE_TYPE, strerror(errno))); close(csock); return(-1); } /* Connect ksocket socket to remote IP address */ if (NgSendMsg(csock, ksockpath, NGM_KSOCKET_COOKIE, NGM_KSOCKET_CONNECT, &peer_addr, peer_addr.ss_len) < 0 && errno != EINPROGRESS) { /* happens in -current (weird) */ Log(LG_ERR, ("[%s] PPTP: can't connect() %s node: %s", p->name, NG_KSOCKET_NODE_TYPE, strerror(errno))); close(csock); return(-1); } /* Configure PPTP/GRE node */ gc.enabled = 1; gc.enableDelayedAck = Enabled(&pi->conf.options, PPTP_CONF_DELAYED_ACK); gc.enableAlwaysAck = Enabled(&pi->conf.options, PPTP_CONF_ALWAYS_ACK); #if NGM_PPTPGRE_COOKIE >= 1082548365 gc.enableWindowing = Enabled(&pi->conf.options, PPTP_CONF_WINDOWING); #endif if (NgSendMsg(csock, pptppath, NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_SET_CONFIG, &gc, sizeof(gc)) < 0) { Log(LG_ERR, ("[%s] PPTP: can't config %s node: %s", p->name, NG_PPTPGRE_NODE_TYPE, strerror(errno))); close(csock); return(-1); } close(csock); /* Done */ return(0); } /* * PptpIncoming() * * The control code calls this function to report that some * remote PPTP client has asked us if we will accept an incoming * call relayed over PPTP. */ static struct pptplinkinfo PptpIncoming(struct pptpctrlinfo *cinfo, struct u_addr *self, struct u_addr *peer, in_port_t port, int bearType, const char *callingNum, const char *calledNum, const char *subAddress) { return(PptpPeerCall(cinfo, self, peer, port, TRUE, callingNum, calledNum, subAddress)); } /* * PptpOutgoing() * * The control code calls this function to report that some * remote PPTP client has asked us if we will dial out to some * phone number. We don't actually do this, but some clients * initiate their connections as outgoing calls for some reason. */ static struct pptplinkinfo PptpOutgoing(struct pptpctrlinfo *cinfo, struct u_addr *self, struct u_addr *peer, in_port_t port, int bearType, int frameType, int minBps, int maxBps, const char *calledNum, const char *subAddress) { return(PptpPeerCall(cinfo, self, peer, port, FALSE, "", calledNum, subAddress)); } /* * PptpPeerCall() * * Peer has initiated a call (either incoming or outgoing; either * way it's the same to us). If we have an available link that may * accept calls from the peer's IP addresss and port, then say yes. */ static struct pptplinkinfo PptpPeerCall(struct pptpctrlinfo *cinfo, struct u_addr *self, struct u_addr *peer, in_port_t port, int incoming, const char *callingNum, const char *calledNum, const char *subAddress) { struct pptplinkinfo linfo; PhysInfo p = NULL; PptpInfo pi = NULL; int k; time_t now = time(NULL); memset(&linfo, 0, sizeof(linfo)); linfo.cookie = NULL; linfo.result = PptpResult; linfo.setLinkInfo = PptpSetLinkInfo; linfo.cancel = PptpCancel; if (gShutdownInProgress) { Log(LG_PHYS, ("Shutdown sequence in progress, ignoring request.")); return(linfo); } if (OVERLOAD()) { Log(LG_PHYS, ("Daemon overloaded, ignoring request.")); return(linfo); } /* Find a suitable link; prefer the link best matching peer's IP address */ for (k = 0; k < gNumPhyses; k++) { PhysInfo p2; PptpInfo pi2; if (gPhyses[k] && gPhyses[k]->type != &gPptpPhysType) continue; p2 = gPhyses[k]; pi2 = (PptpInfo)p2->info; /* See if link is feasible */ if ((p2->state == PHYS_STATE_DOWN) && (now - p2->lastClose) >= PPTP_REOPEN_PAUSE && Enabled(&pi2->conf.options, PPTP_CONF_INCOMING) && (u_addrempty(&pi2->conf.self_addr) || (u_addrcompare(&pi2->conf.self_addr, self) == 0)) && IpAddrInRange(&pi2->conf.peer_addr, peer) && (!pi2->conf.peer_port || pi2->conf.peer_port == port)) { /* Link is feasible; now see if it's preferable */ if (!pi || pi2->conf.peer_addr.width > pi->conf.peer_addr.width) { p = p2; pi = pi2; if (u_rangehost(&pi->conf.peer_addr)) { break; /* Nothing could be better */ } } } } /* If no link is suitable, can't take the call */ if (p == NULL) { Log(LG_PHYS, ("No free PPTP link with requested parameters " "was found")); return(linfo); } Log(LG_PHYS, ("[%s] Accepting PPTP connection", p->name)); PhysIncoming(p); /* Got one */ linfo.cookie = p; p->state = PHYS_STATE_CONNECTING; pi->cinfo = *cinfo; pi->originate = FALSE; pi->outcall = !incoming; pi->sync = 1; pi->self_addr = *self; pi->peer_addr = *peer; pi->peer_port = port; strlcpy(pi->callingnum, callingNum, sizeof(pi->callingnum)); strlcpy(pi->callednum, calledNum, sizeof(pi->callednum)); return(linfo); } /* * PptpCancel() * * The control code calls this function to cancel a * local outgoing call in progress. */ static void PptpCancel(void *cookie) { PptpInfo pi; PhysInfo p; /* It this fake call? */ if (!cookie) return; p = (PhysInfo)cookie; pi = (PptpInfo) p->info; Log(LG_PHYS, ("[%s] PPTP call cancelled in state %s", p->name, gPhysStateNames[p->state])); if (p->state == PHYS_STATE_DOWN) return; PhysDown(p, STR_CON_FAILED0, NULL); p->state = PHYS_STATE_DOWN; u_addrclear(&pi->peer_addr); pi->peer_port = 0; pi->callingnum[0]=0; pi->callednum[0]=0; } /* * PptpListenUpdate() */ static void PptpListenUpdate(PhysInfo p) { PptpInfo pi = (PptpInfo) p->info; if (pi->listener == NULL) { if (Enabled(&pi->conf.options, PPTP_CONF_INCOMING)) { char buf[64]; /* Set up listening for incoming connections */ if ((pi->listener = PptpCtrlListen(&pi->conf.self_addr, pi->conf.self_port)) != NULL) { Log(LG_PHYS, ("PPTP: waiting for connection on %s", u_addrtoa(&pi->conf.self_addr, buf, sizeof(buf)))); } else { Log(LG_ERR, ("PPTP: Error, can't listen for connection!")); } } } else { if (!Enabled(&pi->conf.options, PPTP_CONF_INCOMING)) { PptpCtrlUnListen(pi->listener); pi->listener = NULL; } } } /* * PptpSetCommand() */ static int PptpSetCommand(Context ctx, int ac, char *av[], void *arg) { PptpInfo const pi = (PptpInfo) ctx->phys->info; struct u_range rng; int port; switch ((intptr_t)arg) { case SET_SELFADDR: case SET_PEERADDR: if (ac < 1 || ac > 2 || !ParseRange(av[0], &rng, ALLOW_IPV4|ALLOW_IPV6)) return(-1); if (ac > 1) { if ((port = atoi(av[1])) < 0 || port > 0xffff) return(-1); } else { port = 0; } if ((intptr_t)arg == SET_SELFADDR) { pi->conf.self_addr = rng.addr; pi->conf.self_port = port; } else { pi->conf.peer_addr = rng; pi->conf.peer_port = port; } break; case SET_CALLINGNUM: if (ac != 1) return(-1); snprintf(pi->conf.callingnum, sizeof(pi->conf.callingnum), "%s", av[0]); break; case SET_CALLEDNUM: if (ac != 1) return(-1); snprintf(pi->conf.callednum, sizeof(pi->conf.callednum), "%s", av[0]); break; case SET_ENABLE: EnableCommand(ac, av, &pi->conf.options, gConfList); PptpListenUpdate(ctx->phys); break; case SET_DISABLE: DisableCommand(ac, av, &pi->conf.options, gConfList); PptpListenUpdate(ctx->phys); break; default: assert(0); } return(0); }