#include #include #include #include #include #include #include "dat.h" #include "fns.h" static char sipversion[] = "SIP/2.0"; static char useragent[] = "catphone (plan9front)"; static char *methodstrtab[] = { [REGISTER] "REGISTER", [INVITE] "INVITE", [ACK] "ACK", [BYE] "BYE", [CANCEL] "CANCEL", [OPTIONS] "OPTIONS", [NOTIFY] "NOTIFY", [SUBSCRIBE] "SUBSCRIBE", [INFO] "INFO", [MESSAGE] "MESSAGE", [UPDATE] "UPDATE", [REFER] "REFER", }; static char * getmethodstr(SipMethod m) { return methodstrtab[m]; } static char * md5authfn(char *user, char *pass, char *uri, Sipmsg* m) { uchar h1d[MD5dlen], h2d[MD5dlen], rd[MD5dlen]; char h1ds[2*MD5dlen+1], h2ds[2*MD5dlen+1]; static char rds[2*MD5dlen+1]; char buf[4096]; snprint(buf, sizeof buf, "%s:%s:%s", user, m->auth.realm, pass); md5((uchar*)buf, strlen(buf), h1d, nil); snprint(buf, sizeof buf, "%s:%s", getmethodstr(m->method), uri); md5((uchar*)buf, strlen(buf), h2d, nil); snprint(h1ds, sizeof h1ds, "%.*lH", sizeof h1d, h1d); snprint(h2ds, sizeof h2ds, "%.*lH", sizeof h2d, h2d); snprint(buf, sizeof buf, "%s:%s:%s", h1ds, m->auth.nonce, h2ds); md5((uchar*)buf, strlen(buf), rd, nil); snprint(rds, sizeof rds, "%.*lH", sizeof rd, rd); return rds; } static struct { char *name; char *(*fn)(char*, char*, char*, Sipmsg*); } algos[] = { [AMD5] { .name = "MD5", .fn = md5authfn }, }; static uint hash(char *s) { uint h; h = 0x811c9dc5; while(*s != 0) h = (h^(uchar)*s++) * 0x1000193; return h % 13; } /* rfc3261 ยง 10 - Registrations */ static int sip_register(Sip *s, char *user, char *pass) { Sipmsg *req, *res; Hdr *h; Biobuf *bin, *bout; char *line, *p, *kv[8], *kv2[2], buf[1024]; int n; if((bin = Bfdopen(s->fd, OREAD)) == nil) sysfatal("Bfdopen: %r"); if((bout = Bfdopen(s->fd, OWRITE)) == nil) sysfatal("Bfdopen: %r"); /* present yourself */ req = newsipmsg(); req->method = REGISTER; req->uri = smprint("sip:%s", s->nci->rsys); req->version = sipversion; snprint(buf, sizeof buf, "%s/UDP %s:%s;branch=z9hG4bK703d971c0c737b8e;rport", sipversion, s->nci->lsys, s->nci->lserv); addheader(req, "Via", buf); snprint(buf, sizeof buf, ";expires=3849", user, s->nci->lsys, s->nci->lserv); addheader(req, "Contact", buf); addheader(req, "Max-Forwards", "70"); snprint(buf, sizeof buf, "", user, s->nci->rsys); addheader(req, "To", buf); snprint(buf, sizeof buf, ";tag=4a5a693256d38cbc", user, s->nci->rsys); addheader(req, "From", buf); addheader(req, "Call-ID", "2cee372fc4be4e45"); addheader(req, "CSeq", "16021 REGISTER"); addheader(req, "User-Agent", useragent); addheader(req, "Allow", "INVITE,ACK,BYE,CANCEL,OPTIONS,NOTIFY,SUBSCRIBE,INFO,MESSAGE,UPDATE,REFER"); snprint(buf, sizeof buf, "%lud", req->len); addheader(req, "Content-Length", buf); Bprint(bout, "%S", req); Bflush(bout); if(debug) fprint(2, "sent:\n%S\n", req); delsipmsg(req); /* wait for the challenge */ res = newsipmsg(); while((line = Brdline(bin, '\n')) != nil){ if(strncmp(line, "\r\n", 2) == 0) break; p = strchr(line, '\r'); *p++ = 0, *p = 0; if(strstr(line, ":") == nil && req->code == 0){ if(gettokens(line, kv, 3, " ") == 3){ res->version = strdup(kv[0]); res->code = strtoul(kv[1], nil, 10); res->reason = strdup(kv[2]); } continue; } if(gettokens(line, kv, 2, ": ") == 2) addheader(res, kv[0], kv[1]); } if(debug) fprint(2, "rcvd:\n%S\n", res); /* respond to the challenge */ if((h = getheader(res, "WWW-Authenticate")) == nil) return -1; if((n = gettokens(h->value, kv, nelem(kv), ", ")) == 0) return -1; while(n-- > 0){ if(gettokens(kv[n], kv2, 2, "=\"") != 2) continue; /* XXX: this hack should be replaced by a better method */ p = strchr(kv2[1], '"'); if(p != nil) *p = 0; if(strcmp(kv2[0], "algorithm") == 0) req->auth.algo = strdup(kv2[1]); else if(strcmp(kv2[0], "realm") == 0) req->auth.realm = strdup(kv2[1]); else if(strcmp(kv2[0], "nonce") == 0) req->auth.nonce = strdup(kv2[1]); } if(strcmp(res->auth.algo, "MD5") == 0){ snprint(buf, sizeof buf, "sip:%s", s->nci->rsys); res->auth.response = algos[AMD5].fn(user, pass, buf, res); if(res->auth.response == nil) return -1; }else return -1; req = newsipmsg(); req->method = REGISTER; req->uri = strdup(buf); req->version = sipversion; snprint(buf, sizeof buf, "%s/UDP %s:%s;branch=z9hG4bK703d971c0c737b8e;rport", sipversion, s->nci->lsys, s->nci->lserv); addheader(req, "Via", buf); snprint(buf, sizeof buf, ";expires=3849", user, s->nci->lsys, s->nci->lserv); addheader(req, "Contact", buf); snprint(buf, sizeof buf, "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", algorithm=\"%s\"", user, res->auth.realm, res->auth.nonce, req->uri, res->auth.response, res->auth.algo); addheader(req, "Authorization", buf); addheader(req, "Max-Forwards", "70"); snprint(buf, sizeof buf, "", user, s->nci->rsys); addheader(req, "To", buf); snprint(buf, sizeof buf, ";tag=4a5a693256d38cbc", user, s->nci->rsys); addheader(req, "From", buf); addheader(req, "Call-ID", "2cee372fc4be4e45"); addheader(req, "CSeq", "16022 REGISTER"); addheader(req, "User-Agent", useragent); addheader(req, "Allow", "INVITE,ACK,BYE,CANCEL,OPTIONS,NOTIFY,SUBSCRIBE,INFO,MESSAGE,UPDATE,REFER"); snprint(buf, sizeof buf, "%lud", req->len); addheader(req, "Content-Length", buf); Bprint(bout, "%S", req); Bflush(bout); if(debug) fprint(2, "sent:\n%S\n", req); delsipmsg(res); delsipmsg(req); /* get the OK */ res = newsipmsg(); while((line = Brdline(bin, '\n')) != nil){ if(strncmp(line, "\r\n", 2) == 0) break; p = strchr(line, '\r'); *p++ = 0, *p = 0; if(strstr(line, ":") == nil && req->code == 0){ if(gettokens(line, kv, 3, " ") == 3){ res->version = strdup(kv[0]); res->code = strtoul(kv[1], nil, 10); res->reason = strdup(kv[2]); } continue; } if(gettokens(line, kv, 2, ": ") == 2) addheader(res, kv[0], kv[1]); } if(debug) fprint(2, "rcvd:\n%S\n", res); Bterm(bin); Bterm(bout); return 0; } int Sfmt(Fmt *f) { Sipmsg *m; Hdr *h; int i, n; m = va_arg(f->args, Sipmsg*); n = 0; if(m->code == 0){ /* request */ n += fmtprint(f, "%s %s %s\r\n", getmethodstr(m->method), m->uri, m->version); }else{ /* response */ n += fmtprint(f, "%s %d %s\r\n", m->version, m->code, m->reason); } for(i = 0; i < nelem(m->headers); i++) for(h = m->headers[i]; h != nil; h = h->next) n += fmtprint(f, "%s: %s\r\n", h->name, h->value); n += fmtprint(f, "\r\n"); if(m->len > 0){ fmtprint(f, "%.*s", (int)m->len, m->body); n += m->len; } return n; } void SIPfmtinstall(void) { fmtinstall('S', Sfmt); } void addheader(Hdrtab *ht, char *name, char *value) { Hdr *h, *newh; uint key; key = hash(name); newh = emalloc(sizeof(Hdr)); newh->name = strdup(name); newh->value = strdup(value); newh->next = nil; h = ht->headers[key]; if(h == nil){ ht->headers[key] = newh; return; } while(h->next != nil) h = h->next; h->next = newh; } Hdr * getheader(Hdrtab *ht, char *name) { Hdr *h; uint key; key = hash(name); for(h = ht->headers[key]; h != nil; h = h->next) if(cistrcmp(h->name, name) == 0) return h; return nil; } void delheader(Hdrtab *ht, char *name) { Hdr *h, *nh; uint key; key = hash(name); h = ht->headers[key]; while(h != nil){ nh = h->next; if(cistrcmp(h->name, name) == 0){ free(h->name); free(h->value); free(h); } h = nh; } } void delheaders(Hdrtab *ht) { Hdr *h, *nh; int i; for(i = 0; i < nelem(ht->headers); i++) for(h = ht->headers[i]; h != nil; h = nh){ nh = h->next; free(h->name); free(h->value); free(h); } } Sipmsg * newsipmsg(void) { Sipmsg *m; m = emalloc(sizeof(Sipmsg)); memset(m, 0, sizeof *m); return m; } void delsipmsg(Sipmsg *m) { if(m->uri != nil) free(m->uri); if(m->reason != nil) free(m->reason); delheaders(m); free(m); } Sip * mksip(int fd) { Sip *s; s = emalloc(sizeof(Sip)); memset(s, 0, sizeof *s); s->version = 2; s->nci = getnetconninfo(nil, fd); if(s->nci == nil){ werrstr("couldn't getnetconninfo"); free(s); return nil; } s->fd = fd; s->reg = sip_register; return s; } void rmsip(Sip *s) { close(s->fd); freenetconninfo(s->nci); free(s); }