Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.564 diff -u -r1.564 chan_sip.c --- channels/chan_sip.c 14 Nov 2004 15:13:13 -0000 1.564 +++ channels/chan_sip.c 14 Nov 2004 20:57:20 -0000 @@ -216,8 +216,8 @@ /* sip_request: The data grabbed from the UDP socket */ struct sip_request { - char *rlPart1; /* SIP Method Name or "SIP/2.0" protocol version */ - char *rlPart2; /* The Request URI or Response Status */ + char *rlPart1; /* SIP Method Name or "SIP/2.0" protocol version */ + char *rlPart2; /* The Request URI or Response Status */ int len; int headers; /* SIP Headers */ char *header[SIP_MAX_HEADERS]; @@ -310,7 +310,7 @@ char nonce[256]; /* Authorization nonce */ char opaque[256]; /* Opaque nonsense */ char qop[80]; /* Quality of Protection, since SIP wasn't complicated enough yet. */ - char domain[256]; /* Authorization nonce */ + char domain[256]; /* Authorization domain */ char lastmsg[256]; /* Last Message sent/received */ int amaflags; /* AMA Flags */ int pendinginvite; /* Any pending invite */ @@ -483,7 +483,7 @@ int portno; /* Optional port override */ char username[80]; /* Who we are registering as */ char authuser[80]; /* Who we *authenticate* as */ - char hostname[80]; + char hostname[80]; /* Hostname or domain for registration */ char secret[80]; /* Password or key name in []'s */ char md5secret[80]; char contact[80]; /* Contact extension */ @@ -491,12 +491,22 @@ int expire; /* Sched ID of expiration */ int timeout; /* sched id of sip_reg_timeout */ int refresh; /* How often to refresh */ - struct sip_pvt *call; /* create a sip_pvt structure for each outbound "registration call" in progress */ - int regstate; + struct sip_pvt *call; /* create a sip_pvt structure for each + outbound "registration call" in progress */ + int regstate; /* Registration state */ int callid_valid; /* 0 means we haven't chosen callid for this registry yet. */ char callid[80]; /* Global CallID for this registry */ unsigned int ocseq; /* Sequence number we got to for REGISTERs for this registry */ struct sockaddr_in us; /* Who the server thinks we are */ + + /* Saved headers */ + char realm[256]; /* Authorization realm */ + char nonce[256]; /* Authorization nonce */ + char domain[256]; /* Authorization domain */ + char opaque[256]; /* Opaque nonsense */ + char qop[80]; /* Quality of Protection. */ + + char lastmsg[256]; /* Last Message sent/received */ struct sip_registry *next; }; @@ -3961,6 +3971,10 @@ { /* if we are here, we know that we need to reregister. */ struct sip_registry *r=(struct sip_registry *)data; + + if (sipdebug) + ast_log(LOG_NOTICE, " -- Re-registration for %s@%s\n", r->username, r->hostname); + ast_mutex_lock(®l.lock); r->expire = -1; __sip_do_register(r); @@ -3979,12 +3993,15 @@ /*--- sip_reg_timeout: Registration timeout, register again */ static int sip_reg_timeout(void *data) { + /* if we are here, our registration timed out, so we'll just do it over */ struct sip_registry *r=data; struct sip_pvt *p; int res; + ast_mutex_lock(®l.lock); - ast_log(LOG_NOTICE, "Registration for '%s@%s' timed out, trying again\n", r->username, r->hostname); + + ast_log(LOG_NOTICE, " -- Registration for '%s@%s' timed out, trying again\n", r->username, r->hostname); if (r->call) { /* Unlink us, destroy old call. Locking is not relevent here because all this happens in the single SIP manager thread. */ @@ -3996,14 +4013,14 @@ __sip_pretend_ack(p); } r->regstate=REG_STATE_UNREGISTERED; - manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nDomain: %s\r\nStatus: %s\r\n", r->hostname, regstate2str(r->regstate)); + manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nUser: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate)); r->timeout = -1; res=transmit_register(r, "REGISTER", NULL, NULL); ast_mutex_unlock(®l.lock); return 0; } -/*--- transmit_register: Transmit register to SIP proxy ---*/ +/*--- transmit_register: Transmit register to SIP proxy or UA ---*/ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char *authheader) { struct sip_request req; @@ -4028,31 +4045,37 @@ } else p = r->call; } else { + /* Build callid for registration if we haven't registred before */ if (!r->callid_valid) { build_callid(r->callid, sizeof(r->callid), __ourip, default_fromdomain); r->callid_valid = 1; } + /* Allocate SIP packet for registration */ p=sip_alloc( r->callid, NULL, 0); if (!p) { ast_log(LOG_WARNING, "Unable to allocate registration call\n"); return 0; } + /* Find address to hostname */ if (create_addr(p,r->hostname)) { sip_destroy(p); return 0; } + /* Copy back Call-ID in case create_addr changed it */ strncpy(r->callid, p->callid, sizeof(r->callid) - 1); if (r->portno) p->sa.sin_port = htons(r->portno); - p->outgoing = 1; - r->call=p; - p->registry=r; - if (!ast_strlen_zero(r->secret)) + p->outgoing = 1; /* Registration is outgoing call */ + r->call=p; /* Save pointer to SIP packet */ + p->registry=r; /* Add pointer to registry in packet */ + if (!ast_strlen_zero(r->secret)) /* Secret (password) */ strncpy(p->peersecret, r->secret, sizeof(p->peersecret)-1); if (!ast_strlen_zero(r->md5secret)) strncpy(p->peermd5secret, r->md5secret, sizeof(p->peermd5secret)-1); - if (!ast_strlen_zero(r->authuser)) { + /* User name in this realm + - if authuser is set, use that, otherwise use username */ + if (!ast_strlen_zero(r->authuser)) { strncpy(p->peername, r->authuser, sizeof(p->peername)-1); strncpy(p->authname, r->authuser, sizeof(p->authname)-1); } else { @@ -4064,6 +4087,7 @@ } if (!ast_strlen_zero(r->username)) strncpy(p->username, r->username, sizeof(p->username)-1); + /* Save extension in packet */ strncpy(p->exten, r->contact, sizeof(p->exten) - 1); /* @@ -4079,13 +4103,15 @@ /* set up a timeout */ if (auth==NULL) { if (r->timeout > -1) { - ast_log(LOG_WARNING, "Still have a timeout, %d\n", r->timeout); + ast_log(LOG_WARNING, "Still have a registration timeout, %d\n", r->timeout); ast_sched_del(sched, r->timeout); } r->timeout = ast_sched_add(sched, 20*1000, sip_reg_timeout, r); - ast_log(LOG_DEBUG, "Scheduled a timeout # %d\n", r->timeout); + if (sipdebug) + ast_log(LOG_DEBUG, "Scheduled a registration timeout # %d\n", r->timeout); } + /* Build from: and to: headers for registration */ if (strchr(r->username, '@')) { snprintf(from, sizeof(from), ";tag=as%08x", r->username, p->tag); if (!ast_strlen_zero(p->theirtag)) @@ -4108,6 +4134,7 @@ memset(&req, 0, sizeof(req)); init_req(&req, cmd, addr); + /* Add to CSEQ */ snprintf(tmp, sizeof(tmp), "%u %s", ++r->ocseq, cmd); p->ocseq = r->ocseq; @@ -4122,8 +4149,27 @@ add_header(&req, "Call-ID", p->callid); add_header(&req, "CSeq", tmp); add_header(&req, "User-Agent", default_useragent); - if (auth) + + + if (auth) /* Add auth header */ add_header(&req, authheader, auth); + else if ( !ast_strlen_zero(r->nonce) ) { + char digest[1024]; + + /* We have auth data to reuse, build a digest header! */ + if (sipdebug) + ast_log(LOG_DEBUG, " >>> Re-using Auth data for %s@%s\n", r->username, r->hostname); + strncpy(p->realm, r->realm, sizeof(p->realm)-1); + strncpy(p->nonce, r->nonce, sizeof(p->nonce)-1); + strncpy(p->domain, r->domain, sizeof(p->domain)-1); + strncpy(p->opaque, r->opaque, sizeof(p->opaque)-1); + strncpy(p->qop, r->qop, sizeof(p->qop)-1); + + memset(digest,0,sizeof(digest)); + build_reply_digest(p, "REGISTER", digest, sizeof(digest)); + add_header(&req, "Authorization", digest); + + } snprintf(tmp, sizeof(tmp), "%d", default_expiry); add_header(&req, "Expires", tmp); @@ -4218,6 +4264,7 @@ if (*p->realm) { char digest[1024]; + memset(digest,0,sizeof(digest)); build_reply_digest(p, msg, digest, sizeof(digest)); add_header(&resp, "Proxy-Authorization", digest); @@ -6201,31 +6248,39 @@ static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *digest, int digest_len); -/*--- do_register_auth: Challenge for registration ---*/ +/*--- do_register_auth: Authenticate for outbound registration ---*/ static int do_register_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader) { char digest[1024]; + p->authtries++; memset(digest,0,sizeof(digest)); - if (reply_digest(p,req, header, "REGISTER", digest, sizeof(digest))) { - /* There's nothing to use for authentication */ + if (reply_digest(p, req, header, "REGISTER", digest, sizeof(digest))) { + /* No digest challenge in request */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "No authentication challenge, sending blank registration to domain/host name %s\n", p->registry->hostname); + /* No old challenge */ return -1; } + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Responding to challenge, registration to domain/host name %s\n", p->registry->hostname); + /* Send digest auth */ return transmit_register(p->registry,"REGISTER",digest, respheader); } -/*--- do_proxy_auth: Challenge user ---*/ +/*--- do_proxy_auth: Add authentication on outbound SIP packet ---*/ static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *msg, int init) { char digest[1024]; p->authtries++; memset(digest,0,sizeof(digest)); - if (reply_digest(p,req, header, msg, digest, sizeof(digest) )) { + if (reply_digest(p, req, header, msg, digest, sizeof(digest) )) { /* No way to authenticate */ return -1; } + /* Now we have a reply digest */ return transmit_invite(p,msg,!strcasecmp(msg, "INVITE"),digest, respheader, NULL,NULL,NULL, init); } -/*--- reply_digest: reply to authentication for outbound registrations ---*/ +/*--- reply_digest: reply to authentication challenge for outbound registrations ---*/ /* This is used for register= servers in sip.conf, SIP proxies we register with for receiving calls from. */ static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, char *orig_header, char *digest, int digest_len) { @@ -6317,6 +6372,15 @@ strncpy(p->domain, domain, sizeof(p->domain)-1); strncpy(p->opaque, opaque, sizeof(p->opaque)-1); strncpy(p->qop, qop, sizeof(p->qop)-1); + + /* Save auth data for following registrations */ + if (p->registry) { + strncpy(p->registry->realm, realm, sizeof(p->realm)-1); + strncpy(p->registry->nonce, nonce, sizeof(p->nonce)-1); + strncpy(p->registry->domain, domain, sizeof(p->domain)-1); + strncpy(p->registry->opaque, opaque, sizeof(p->opaque)-1); + strncpy(p->registry->qop, qop, sizeof(p->qop)-1); + } build_reply_digest(p, orig_header, digest, digest_len); return 0; } @@ -6721,20 +6785,25 @@ if (!expires) expires=atoi(get_header(req, "expires")); if (!expires) expires=default_expiry; + expires_ms = expires * 1000; if (expires <= EXPIRY_GUARD_LIMIT) expires_ms -= MAX((expires_ms * EXPIRY_GUARD_PCT),EXPIRY_GUARD_MIN); else expires_ms -= EXPIRY_GUARD_SECS * 1000; + if (sipdebug) + ast_log(LOG_NOTICE, "Outbound Registration: Expiry for %s is %d sec (Scheduling reregistration in %d ms)\n", r->hostname, expires, expires_ms); r->refresh= (int) expires_ms / 1000; + + /* Schedule re-registration before we expire */ r->expire=ast_sched_add(sched, expires_ms, sip_reregister, r); } else ast_log(LOG_WARNING, "Got 200 OK on REGISTER that isn't a register\n"); } break; - case 401: /* Not authorized on REGISTER */ + case 401: /* Not www-authorized on REGISTER */ if (!strcasecmp(msg, "INVITE")) { /* First we ACK */ transmit_request(p, "ACK", seqno, 0, 0); @@ -6751,7 +6820,22 @@ } else p->needdestroy = 1; break; - case 407: + case 403: /* Forbidden - we failed authentication */ + /* Should we ack a 403 ?????????????? */ + if (!strcasecmp(msg, "INVITE")) { + + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for INVITE to '%s'\n", get_header(&p->initreq, "From")); + if (owner) + ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + p->needdestroy = 1; + } else if (p->registry && !strcasecmp(msg, "REGISTER")) { + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for REGISTER for '%s'\n", p->registry->username, p->registry->hostname); + p->needdestroy = 1; + } else { + ast_log(LOG_WARNING, "Forbidden - wrong password on authentication for %s\n", msg); + } + break; + case 407: /* Proxy auth required */ if (!strcasecmp(msg, "INVITE")) { /* First we ACK */ transmit_request(p, "ACK", seqno, 0, 0);