Changeset 3321 for pjproject/trunk/pjlib-util/src/pjlib-util/http_client.c
- Timestamp:
- Sep 27, 2010 8:35:08 AM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib-util/src/pjlib-util/http_client.c
r3236 r3321 21 21 #include <pj/activesock.h> 22 22 #include <pj/assert.h> 23 #include <pj/ctype.h> 23 24 #include <pj/errno.h> 24 25 #include <pj/except.h> … … 26 27 #include <pj/string.h> 27 28 #include <pj/timer.h> 29 #include <pjlib-util/base64.h> 28 30 #include <pjlib-util/errno.h> 31 #include <pjlib-util/md5.h> 29 32 #include <pjlib-util/scanner.h> 33 #include <pjlib-util/string.h> 30 34 31 35 #if 0 … … 40 44 #define HTTP_1_0 "1.0" 41 45 #define HTTP_1_1 "1.1" 42 #define HTTP_SEPARATOR "://"43 46 #define CONTENT_LENGTH "Content-Length" 44 47 /* Buffer size for sending/receiving messages. */ … … 94 97 READING_COMPLETE, 95 98 ABORTING, 99 }; 100 101 enum auth_state 102 { 103 AUTH_NONE, /* Not authenticating */ 104 AUTH_RETRYING, /* New request with auth has been submitted */ 105 AUTH_DONE /* Done retrying the request with auth. */ 96 106 }; 97 107 … … 110 120 pj_str_t buffer; /* Buffer to send/receive msgs */ 111 121 enum http_state state; /* State of the HTTP request */ 122 enum auth_state auth_state; /* Authentication state */ 112 123 pj_timer_entry timer_entry;/* Timer entry */ 113 124 pj_bool_t resolved; /* Whether URL's host is resolved */ … … 143 154 void *data, pj_size_t size, 144 155 pj_size_t *remainder); 156 /* Restart the request with authentication */ 157 static void restart_req_with_auth(pj_http_req *hreq); 158 /* Parse authentication challenge */ 159 static pj_status_t parse_auth_chal(pj_pool_t *pool, pj_str_t *input, 160 pj_http_auth_chal *chal); 145 161 146 162 static pj_uint16_t get_http_default_port(const pj_str_t *protocol) … … 319 335 hreq->response.size = size - rem; 320 336 } 337 338 /* If code is 401 or 407, find and parse WWW-Authenticate or 339 * Proxy-Authenticate header 340 */ 341 if (hreq->response.status_code == 401 || 342 hreq->response.status_code == 407) 343 { 344 const pj_str_t STR_WWW_AUTH = { "WWW-Authenticate", 16 }; 345 const pj_str_t STR_PROXY_AUTH = { "Proxy-Authenticate", 18 }; 346 pj_http_resp *response = &hreq->response; 347 pj_http_headers *hdrs = &response->headers; 348 unsigned i; 349 350 status = PJ_ENOTFOUND; 351 for (i = 0; i < hdrs->count; i++) { 352 if (!pj_stricmp(&hdrs->header[i].name, &STR_WWW_AUTH) || 353 !pj_stricmp(&hdrs->header[i].name, &STR_PROXY_AUTH)) 354 { 355 status = parse_auth_chal(hreq->pool, 356 &hdrs->header[i].value, 357 &response->auth_chal); 358 break; 359 } 360 } 361 362 /* Check if we should perform authentication */ 363 if (status == PJ_SUCCESS && 364 hreq->auth_state == AUTH_NONE && 365 hreq->response.auth_chal.scheme.slen && 366 hreq->param.auth_cred.username.slen && 367 (hreq->param.auth_cred.scheme.slen == 0 || 368 !pj_stricmp(&hreq->response.auth_chal.scheme, 369 &hreq->param.auth_cred.scheme)) && 370 (hreq->param.auth_cred.realm.slen == 0 || 371 !pj_stricmp(&hreq->response.auth_chal.realm, 372 &hreq->param.auth_cred.realm)) 373 ) 374 { 375 /* Yes, authentication is required and we have been 376 * configured with credential. 377 */ 378 restart_req_with_auth(hreq); 379 if (hreq->auth_state == AUTH_RETRYING) { 380 /* We'll be resending the request with auth. This 381 * connection has been closed. 382 */ 383 return PJ_FALSE; 384 } 385 } 386 } 387 321 388 /* We already received the response header, call the 322 * appropriate callback. 389 * appropriate callback. 323 390 */ 324 391 if (hreq->cb.on_response) … … 385 452 (status == PJ_EEOF && hreq->response.content_length == -1)) 386 453 { 387 454 /* Finish reading */ 388 455 http_req_end_request(hreq); 389 456 hreq->response.size = hreq->tcp_state.current_read_size; … … 434 501 } 435 502 503 /* Parse authentication challenge */ 504 static pj_status_t parse_auth_chal(pj_pool_t *pool, pj_str_t *input, 505 pj_http_auth_chal *chal) 506 { 507 pj_scanner scanner; 508 const pj_str_t REALM_STR = { "realm", 5}, 509 NONCE_STR = { "nonce", 5}, 510 ALGORITHM_STR = { "algorithm", 9 }, 511 STALE_STR = { "stale", 5}, 512 QOP_STR = { "qop", 3}, 513 OPAQUE_STR = { "opaque", 6}; 514 pj_status_t status = PJ_SUCCESS; 515 PJ_USE_EXCEPTION ; 516 517 pj_scan_init(&scanner, input->ptr, input->slen, PJ_SCAN_AUTOSKIP_WS, 518 &on_syntax_error); 519 PJ_TRY { 520 /* Get auth scheme */ 521 if (*scanner.curptr == '"') { 522 pj_scan_get_quote(&scanner, '"', '"', &chal->scheme); 523 chal->scheme.ptr++; 524 chal->scheme.slen -= 2; 525 } else { 526 pj_scan_get_until_chr(&scanner, " \t\r\n", &chal->scheme); 527 } 528 529 /* Loop parsing all parameters */ 530 for (;;) { 531 const char *end_param = ", \t\r\n;"; 532 pj_str_t name, value; 533 534 /* Get pair of parameter name and value */ 535 value.ptr = NULL; 536 value.slen = 0; 537 pj_scan_get_until_chr(&scanner, "=, \t\r\n", &name); 538 if (*scanner.curptr == '=') { 539 pj_scan_get_char(&scanner); 540 if (!pj_scan_is_eof(&scanner)) { 541 if (*scanner.curptr == '"' || *scanner.curptr == '\'') { 542 int quote_char = *scanner.curptr; 543 pj_scan_get_quote(&scanner, quote_char, quote_char, 544 &value); 545 value.ptr++; 546 value.slen -= 2; 547 } else if (!strchr(end_param, *scanner.curptr)) { 548 pj_scan_get_until_chr(&scanner, end_param, &value); 549 } 550 } 551 value = pj_str_unescape(pool, &value); 552 } 553 554 if (!pj_stricmp(&name, &REALM_STR)) { 555 chal->realm = value; 556 557 } else if (!pj_stricmp(&name, &NONCE_STR)) { 558 chal->nonce = value; 559 560 } else if (!pj_stricmp(&name, &ALGORITHM_STR)) { 561 chal->algorithm = value; 562 563 } else if (!pj_stricmp(&name, &OPAQUE_STR)) { 564 chal->opaque = value; 565 566 } else if (!pj_stricmp(&name, &QOP_STR)) { 567 chal->qop = value; 568 569 } else if (!pj_stricmp(&name, &STALE_STR)) { 570 chal->stale = value.slen && 571 (*value.ptr != '0') && 572 (*value.ptr != 'f') && 573 (*value.ptr != 'F'); 574 575 } 576 577 /* Eat comma */ 578 if (!pj_scan_is_eof(&scanner) && *scanner.curptr == ',') 579 pj_scan_get_char(&scanner); 580 else 581 break; 582 } 583 584 } 585 PJ_CATCH_ANY { 586 status = PJ_GET_EXCEPTION(); 587 pj_bzero(chal, sizeof(*chal)); 588 TRACE_((THIS_FILE, "Error: parsing of auth header failed")); 589 } 590 PJ_END; 591 pj_scan_fini(&scanner); 592 return status; 593 } 594 436 595 /* The same as #pj_http_headers_add_elmt() with char * as 437 596 * its parameters. … … 465 624 pj_size_t i; 466 625 char *cptr; 467 char * newdata;626 char *end_status, *newdata; 468 627 pj_scanner scanner; 469 628 pj_str_t s; 629 const pj_str_t STR_CONTENT_LENGTH = { CONTENT_LENGTH, 14 }; 470 630 pj_status_t status; 471 631 … … 518 678 PJ_END; 519 679 680 end_status = scanner.curptr; 681 pj_scan_fini(&scanner); 682 520 683 /* Parse the response headers. */ 521 size = i - 2 - ( scanner.curptr- newdata);684 size = i - 2 - (end_status - newdata); 522 685 if (size > 0) { 523 status = http_headers_parse( scanner.curptr + 1, size,686 status = http_headers_parse(end_status + 1, size, 524 687 &response->headers); 525 688 } else { … … 529 692 /* Find content-length header field. */ 530 693 for (i = 0; i < response->headers.count; i++) { 531 if (!pj_stricmp 2(&response->headers.header[i].name,532 CONTENT_LENGTH))694 if (!pj_stricmp(&response->headers.header[i].name, 695 &STR_CONTENT_LENGTH)) 533 696 { 534 697 response->content_length = … … 545 708 } 546 709 } 547 548 pj_scan_fini(&scanner);549 710 550 711 return status; … … 615 776 if (!len) return -1; 616 777 778 pj_bzero(hurl, sizeof(*hurl)); 617 779 pj_scan_init(&scanner, url->ptr, url->slen, 0, &on_syntax_error); 618 780 … … 635 797 } 636 798 637 if (pj_scan_strcmp(&scanner, HTTP_SEPARATOR, 638 pj_ansi_strlen(HTTP_SEPARATOR))) 639 { 799 if (pj_scan_strcmp(&scanner, "://", 3)) { 640 800 PJ_THROW(PJLIB_UTIL_EHTTPINURL); // no "://" after protocol name 641 801 } 642 pj_scan_advance_n(&scanner, pj_ansi_strlen(HTTP_SEPARATOR), PJ_FALSE); 802 pj_scan_advance_n(&scanner, 3, PJ_FALSE); 803 804 if (pj_memchr(url->ptr, '@', url->slen)) { 805 /* Parse username and password */ 806 pj_scan_get_until_chr(&scanner, ":@", &hurl->username); 807 if (*scanner.curptr == ':') { 808 pj_scan_get_char(&scanner); 809 pj_scan_get_until_chr(&scanner, "@", &hurl->passwd); 810 } else { 811 hurl->passwd.slen = 0; 812 } 813 pj_scan_get_char(&scanner); 814 } 643 815 644 816 /* Parse the host and port number (if any) */ 645 817 pj_scan_get_until_chr(&scanner, ":/", &s); 646 818 pj_strassign(&hurl->host, &s); 819 if (hurl->host.slen==0) 820 PJ_THROW(PJ_EINVAL); 647 821 if (pj_scan_is_eof(&scanner) || *scanner.curptr == '/') { 648 822 /* No port number specified */ … … 693 867 pj_pool_t *own_pool; 694 868 pj_http_req *hreq; 869 char *at_pos; 695 870 pj_status_t status; 696 871 … … 737 912 738 913 /* Parse the URL */ 739 if (!pj_strdup(hreq->pool, &hreq->url, url)) 914 if (!pj_strdup_with_null(hreq->pool, &hreq->url, url)) { 915 pj_pool_release(hreq->pool); 740 916 return PJ_ENOMEM; 917 } 741 918 status = pj_http_req_parse_url(&hreq->url, &hreq->hurl); 742 if (status != PJ_SUCCESS) 919 if (status != PJ_SUCCESS) { 920 pj_pool_release(hreq->pool); 743 921 return status; // Invalid URL supplied 922 } 923 924 /* If URL contains username/password, move them to credential and 925 * remove them from the URL. 926 */ 927 if ((at_pos=pj_strchr(&hreq->url, '@')) != NULL) { 928 pj_str_t tmp; 929 char *user_pos = pj_strchr(&hreq->url, '/'); 930 int removed_len; 931 932 /* Save credential first, unescape the string */ 933 tmp = pj_str_unescape(hreq->pool, &hreq->hurl.username);; 934 pj_strdup(hreq->pool, &hreq->param.auth_cred.username, &tmp); 935 936 tmp = pj_str_unescape(hreq->pool, &hreq->hurl.passwd); 937 pj_strdup(hreq->pool, &hreq->param.auth_cred.data, &tmp); 938 939 hreq->hurl.username.ptr = hreq->hurl.passwd.ptr = NULL; 940 hreq->hurl.username.slen = hreq->hurl.passwd.slen = 0; 941 942 /* Remove "username:password@" from the URL */ 943 pj_assert(user_pos != 0 && user_pos < at_pos); 944 user_pos += 2; 945 removed_len = at_pos + 1 - user_pos; 946 pj_memmove(user_pos, at_pos+1, hreq->url.ptr+hreq->url.slen-at_pos-1); 947 hreq->url.slen -= removed_len; 948 949 /* Need to adjust hostname and path pointers due to memmove*/ 950 if (hreq->hurl.host.ptr > user_pos && 951 hreq->hurl.host.ptr < user_pos + hreq->url.slen) 952 { 953 hreq->hurl.host.ptr -= removed_len; 954 } 955 /* path may come from a string constant, don't shift it if so */ 956 if (hreq->hurl.path.ptr > user_pos && 957 hreq->hurl.path.ptr < user_pos + hreq->url.slen) 958 { 959 hreq->hurl.path.ptr -= removed_len; 960 } 961 } 744 962 745 963 *http_req = hreq; … … 771 989 PJ_ASSERT_RETURN(http_req->state == IDLE, PJ_EBUSY); 772 990 991 /* Reset few things to make sure restarting works */ 773 992 http_req->error = 0; 993 http_req->response.headers.count = 0; 994 pj_bzero(&http_req->tcp_state, sizeof(http_req->tcp_state)); 774 995 775 996 if (!http_req->resolved) { … … 838 1059 } 839 1060 840 #define STR_PREC(s) s.slen, s.ptr 1061 /* Respond to basic authentication challenge */ 1062 static pj_status_t auth_respond_basic(pj_http_req *hreq) 1063 { 1064 /* Basic authentication: 1065 * credentials = "Basic" basic-credentials 1066 * basic-credentials = base64-user-pass 1067 * base64-user-pass = <base64 [4] encoding of user-pass> 1068 * user-pass = userid ":" password 1069 * 1070 * Sample: 1071 * Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== 1072 */ 1073 pj_str_t user_pass; 1074 pj_http_header_elmt *phdr; 1075 int len; 1076 1077 /* Use send buffer to store userid ":" password */ 1078 user_pass.ptr = hreq->buffer.ptr; 1079 pj_strcpy(&user_pass, &hreq->param.auth_cred.username); 1080 pj_strcat2(&user_pass, ":"); 1081 pj_strcat(&user_pass, &hreq->param.auth_cred.data); 1082 1083 /* Create Authorization header */ 1084 phdr = &hreq->param.headers.header[hreq->param.headers.count++]; 1085 pj_bzero(phdr, sizeof(*phdr)); 1086 if (hreq->response.status_code == 401) 1087 phdr->name = pj_str("Authorization"); 1088 else 1089 phdr->name = pj_str("Proxy-Authorization"); 1090 1091 len = PJ_BASE256_TO_BASE64_LEN(user_pass.slen) + 10; 1092 phdr->value.ptr = (char*)pj_pool_alloc(hreq->pool, len); 1093 phdr->value.slen = 0; 1094 1095 pj_strcpy2(&phdr->value, "Basic "); 1096 len -= phdr->value.slen; 1097 pj_base64_encode((pj_uint8_t*)user_pass.ptr, (int)user_pass.slen, 1098 phdr->value.ptr + phdr->value.slen, &len); 1099 phdr->value.slen += len; 1100 1101 return PJ_SUCCESS; 1102 } 1103 1104 /** Length of digest string. */ 1105 #define MD5_STRLEN 32 1106 /* A macro just to get rid of type mismatch between char and unsigned char */ 1107 #define MD5_APPEND(pms,buf,len) pj_md5_update(pms, (const pj_uint8_t*)buf, len) 1108 1109 /* Transform digest to string. 1110 * output must be at least PJSIP_MD5STRLEN+1 bytes. 1111 * 1112 * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED! 1113 */ 1114 static void digest2str(const unsigned char digest[], char *output) 1115 { 1116 int i; 1117 for (i = 0; i<16; ++i) { 1118 pj_val_to_hex_digit(digest[i], output); 1119 output += 2; 1120 } 1121 } 1122 1123 static void auth_create_digest_response(pj_str_t *result, 1124 const pj_http_auth_cred *cred, 1125 const pj_str_t *nonce, 1126 const pj_str_t *nc, 1127 const pj_str_t *cnonce, 1128 const pj_str_t *qop, 1129 const pj_str_t *uri, 1130 const pj_str_t *realm, 1131 const pj_str_t *method) 1132 { 1133 char ha1[MD5_STRLEN]; 1134 char ha2[MD5_STRLEN]; 1135 unsigned char digest[16]; 1136 pj_md5_context pms; 1137 1138 pj_assert(result->slen >= MD5_STRLEN); 1139 1140 TRACE_((THIS_FILE, "Begin creating digest")); 1141 1142 if (cred->data_type == 0) { 1143 /*** 1144 *** ha1 = MD5(username ":" realm ":" password) 1145 ***/ 1146 pj_md5_init(&pms); 1147 MD5_APPEND( &pms, cred->username.ptr, cred->username.slen); 1148 MD5_APPEND( &pms, ":", 1); 1149 MD5_APPEND( &pms, realm->ptr, realm->slen); 1150 MD5_APPEND( &pms, ":", 1); 1151 MD5_APPEND( &pms, cred->data.ptr, cred->data.slen); 1152 pj_md5_final(&pms, digest); 1153 1154 digest2str(digest, ha1); 1155 1156 } else if (cred->data_type == 1) { 1157 pj_assert(cred->data.slen == 32); 1158 pj_memcpy( ha1, cred->data.ptr, cred->data.slen ); 1159 } else { 1160 pj_assert(!"Invalid data_type"); 1161 } 1162 1163 TRACE_((THIS_FILE, " ha1=%.32s", ha1)); 1164 1165 /*** 1166 *** ha2 = MD5(method ":" req_uri) 1167 ***/ 1168 pj_md5_init(&pms); 1169 MD5_APPEND( &pms, method->ptr, method->slen); 1170 MD5_APPEND( &pms, ":", 1); 1171 MD5_APPEND( &pms, uri->ptr, uri->slen); 1172 pj_md5_final(&pms, digest); 1173 digest2str(digest, ha2); 1174 1175 TRACE_((THIS_FILE, " ha2=%.32s", ha2)); 1176 1177 /*** 1178 *** When qop is not used: 1179 *** response = MD5(ha1 ":" nonce ":" ha2) 1180 *** 1181 *** When qop=auth is used: 1182 *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) 1183 ***/ 1184 pj_md5_init(&pms); 1185 MD5_APPEND( &pms, ha1, MD5_STRLEN); 1186 MD5_APPEND( &pms, ":", 1); 1187 MD5_APPEND( &pms, nonce->ptr, nonce->slen); 1188 if (qop && qop->slen != 0) { 1189 MD5_APPEND( &pms, ":", 1); 1190 MD5_APPEND( &pms, nc->ptr, nc->slen); 1191 MD5_APPEND( &pms, ":", 1); 1192 MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); 1193 MD5_APPEND( &pms, ":", 1); 1194 MD5_APPEND( &pms, qop->ptr, qop->slen); 1195 } 1196 MD5_APPEND( &pms, ":", 1); 1197 MD5_APPEND( &pms, ha2, MD5_STRLEN); 1198 1199 /* This is the final response digest. */ 1200 pj_md5_final(&pms, digest); 1201 1202 /* Convert digest to string and store in chal->response. */ 1203 result->slen = MD5_STRLEN; 1204 digest2str(digest, result->ptr); 1205 1206 TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); 1207 TRACE_((THIS_FILE, "Digest created")); 1208 } 1209 1210 /* Find out if qop offer contains "auth" token */ 1211 static pj_bool_t auth_has_qop( pj_pool_t *pool, const pj_str_t *qop_offer) 1212 { 1213 pj_str_t qop; 1214 char *p; 1215 1216 pj_strdup_with_null( pool, &qop, qop_offer); 1217 p = qop.ptr; 1218 while (*p) { 1219 *p = (char)pj_tolower(*p); 1220 ++p; 1221 } 1222 1223 p = qop.ptr; 1224 while (*p) { 1225 if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') { 1226 int e = *(p+4); 1227 if (e=='"' || e==',' || e==0) 1228 return PJ_TRUE; 1229 else 1230 p += 4; 1231 } else { 1232 ++p; 1233 } 1234 } 1235 1236 return PJ_FALSE; 1237 } 1238 1239 #define STR_PREC(s) (int)(s).slen, (s).ptr 1240 1241 /* Respond to digest authentication */ 1242 static pj_status_t auth_respond_digest(pj_http_req *hreq) 1243 { 1244 const pj_http_auth_chal *chal = &hreq->response.auth_chal; 1245 const pj_http_auth_cred *cred = &hreq->param.auth_cred; 1246 pj_http_header_elmt *phdr; 1247 char digest_response_buf[MD5_STRLEN]; 1248 int len; 1249 pj_str_t digest_response; 1250 1251 /* Check algorithm is supported. We only support MD5 */ 1252 if (chal->algorithm.slen!=0 && 1253 pj_stricmp2(&chal->algorithm, "MD5")) 1254 { 1255 TRACE_((THIS_FILE, "Error: Unsupported digest algorithm \"%.*s\"", 1256 chal->algorithm.slen, chal->algorithm.ptr)); 1257 return PJ_ENOTSUP; 1258 } 1259 1260 /* Add Authorization header */ 1261 phdr = &hreq->param.headers.header[hreq->param.headers.count++]; 1262 pj_bzero(phdr, sizeof(*phdr)); 1263 if (hreq->response.status_code == 401) 1264 phdr->name = pj_str("Authorization"); 1265 else 1266 phdr->name = pj_str("Proxy-Authorization"); 1267 1268 /* Allocate space for the header */ 1269 len = 8 + /* Digest */ 1270 16 + hreq->param.auth_cred.username.slen + /* username= */ 1271 12 + chal->realm.slen + /* realm= */ 1272 12 + chal->nonce.slen + /* nonce= */ 1273 8 + hreq->hurl.path.slen + /* uri= */ 1274 16 + /* algorithm=MD5 */ 1275 16 + MD5_STRLEN + /* response= */ 1276 12 + /* qop=auth */ 1277 8 + /* nc=.. */ 1278 30 + /* cnonce= */ 1279 0; 1280 phdr->value.ptr = (char*)pj_pool_alloc(hreq->pool, len); 1281 1282 /* Configure buffer to temporarily store the digest */ 1283 digest_response.ptr = digest_response_buf; 1284 digest_response.slen = MD5_STRLEN; 1285 1286 if (chal->qop.slen == 0) { 1287 const pj_str_t STR_MD5 = { "MD5", 3 }; 1288 1289 /* Server doesn't require quality of protection. */ 1290 auth_create_digest_response(&digest_response, cred, 1291 &chal->nonce, NULL, NULL, NULL, 1292 &hreq->hurl.path, &chal->realm, 1293 &hreq->param.method); 1294 1295 len = pj_ansi_snprintf( 1296 phdr->value.ptr, len, 1297 "Digest username=\"%.*s\", " 1298 "realm=\"%.*s\", " 1299 "nonce=\"%.*s\", " 1300 "uri=\"%.*s\", " 1301 "algorithm=%.*s, " 1302 "response=\"%.*s\"", 1303 STR_PREC(cred->username), 1304 STR_PREC(chal->realm), 1305 STR_PREC(chal->nonce), 1306 STR_PREC(hreq->hurl.path), 1307 STR_PREC(STR_MD5), 1308 STR_PREC(digest_response)); 1309 if (len < 0) 1310 return PJ_ETOOSMALL; 1311 phdr->value.slen = len; 1312 1313 } else if (auth_has_qop(hreq->pool, &chal->qop)) { 1314 /* Server requires quality of protection. 1315 * We respond with selecting "qop=auth" protection. 1316 */ 1317 const pj_str_t STR_MD5 = { "MD5", 3 }; 1318 const pj_str_t qop = pj_str("auth"); 1319 const pj_str_t nc = pj_str("1"); 1320 const pj_str_t cnonce = pj_str("b39971"); 1321 1322 auth_create_digest_response(&digest_response, cred, 1323 &chal->nonce, &nc, &cnonce, &qop, 1324 &hreq->hurl.path, &chal->realm, 1325 &hreq->param.method); 1326 len = pj_ansi_snprintf( 1327 phdr->value.ptr, len, 1328 "Digest username=\"%.*s\", " 1329 "realm=\"%.*s\", " 1330 "nonce=\"%.*s\", " 1331 "uri=\"%.*s\", " 1332 "algorithm=%.*s, " 1333 "response=\"%.*s\", " 1334 "qop=%.*s, " 1335 "nc=%.*s, " 1336 "cnonce=\"%.*s\"", 1337 STR_PREC(cred->username), 1338 STR_PREC(chal->realm), 1339 STR_PREC(chal->nonce), 1340 STR_PREC(hreq->hurl.path), 1341 STR_PREC(STR_MD5), 1342 STR_PREC(digest_response), 1343 STR_PREC(qop), 1344 STR_PREC(nc), 1345 STR_PREC(cnonce)); 1346 if (len < 0) 1347 return PJ_ETOOSMALL; 1348 phdr->value.slen = len; 1349 1350 } else { 1351 /* Server requires quality protection that we don't support. */ 1352 TRACE_((THIS_FILE, "Error: Unsupported qop offer %.*s", 1353 chal->qop.slen, chal->qop.ptr)); 1354 return PJ_ENOTSUP; 1355 } 1356 1357 return PJ_SUCCESS; 1358 } 1359 1360 1361 static void restart_req_with_auth(pj_http_req *hreq) 1362 { 1363 pj_http_auth_chal *chal = &hreq->response.auth_chal; 1364 pj_http_auth_cred *cred = &hreq->param.auth_cred; 1365 pj_status_t status; 1366 1367 if (hreq->param.headers.count >= PJ_HTTP_HEADER_SIZE) { 1368 TRACE_((THIS_FILE, "Error: no place to put Authorization header")); 1369 hreq->auth_state = AUTH_DONE; 1370 return; 1371 } 1372 1373 /* If credential specifies specific scheme, make sure they match */ 1374 if (cred->scheme.slen && pj_stricmp(&chal->scheme, &cred->scheme)) { 1375 status = PJ_ENOTSUP; 1376 TRACE_((THIS_FILE, "Error: auth schemes mismatch")); 1377 goto on_error; 1378 } 1379 1380 /* If credential specifies specific realm, make sure they match */ 1381 if (cred->realm.slen && pj_stricmp(&chal->realm, &cred->realm)) { 1382 status = PJ_ENOTSUP; 1383 TRACE_((THIS_FILE, "Error: auth realms mismatch")); 1384 goto on_error; 1385 } 1386 1387 if (!pj_stricmp2(&chal->scheme, "basic")) { 1388 status = auth_respond_basic(hreq); 1389 } else if (!pj_stricmp2(&chal->scheme, "digest")) { 1390 status = auth_respond_digest(hreq); 1391 } else { 1392 TRACE_((THIS_FILE, "Error: unsupported HTTP auth scheme")); 1393 status = PJ_ENOTSUP; 1394 } 1395 1396 if (status != PJ_SUCCESS) 1397 goto on_error; 1398 1399 http_req_end_request(hreq); 1400 1401 status = pj_http_req_start(hreq); 1402 if (status != PJ_SUCCESS) 1403 goto on_error; 1404 1405 hreq->auth_state = AUTH_RETRYING; 1406 return; 1407 1408 on_error: 1409 hreq->auth_state = AUTH_DONE; 1410 } 1411 841 1412 842 1413 /* snprintf() to a pj_str_t struct with an option to append the
Note: See TracChangeset
for help on using the changeset viewer.