1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <ip.h> 5 #include <plumb.h> 6 #include <thread.h> 7 #include <fcall.h> 8 #include <9p.h> 9 #include <libsec.h> 10 #include <auth.h> 11 #include "dat.h" 12 #include "fns.h" 13 14 char PostContentType[] = "application/x-www-form-urlencoded"; 15 int httpdebug; 16 17 typedef struct HttpState HttpState; 18 struct HttpState 19 { 20 int fd; 21 Client *c; 22 char *location; 23 char *setcookie; 24 char *netaddr; 25 char *credentials; 26 char autherror[ERRMAX]; 27 Ibuf b; 28 }; 29 30 static void 31 location(HttpState *hs, char *value) 32 { 33 if(hs->location == nil) 34 hs->location = estrdup(value); 35 } 36 37 static void 38 contenttype(HttpState *hs, char *value) 39 { 40 if(hs->c->contenttype != nil) 41 free(hs->c->contenttype); 42 hs->c->contenttype = estrdup(value); 43 } 44 45 static void 46 setcookie(HttpState *hs, char *value) 47 { 48 char *s, *t; 49 Fmt f; 50 51 s = hs->setcookie; 52 fmtstrinit(&f); 53 if(s) 54 fmtprint(&f, "%s", s); 55 fmtprint(&f, "set-cookie: "); 56 fmtprint(&f, "%s", value); 57 fmtprint(&f, "\n"); 58 t = fmtstrflush(&f); 59 if(t){ 60 free(s); 61 hs->setcookie = t; 62 } 63 } 64 65 static char* 66 unquote(char *s, char **ps) 67 { 68 char *p; 69 70 if(*s != '"'){ 71 p = strpbrk(s, " \t\r\n"); 72 *p++ = 0; 73 *ps = p; 74 return s; 75 } 76 for(p=s+1; *p; p++){ 77 if(*p == '\"'){ 78 *p++ = 0; 79 break; 80 } 81 if(*p == '\\' && *(p+1)){ 82 p++; 83 continue; 84 } 85 } 86 memmove(s, s+1, p-(s+1)); 87 s[p-(s+1)] = 0; 88 *ps = p; 89 return s; 90 } 91 92 static char* 93 servername(char *addr) 94 { 95 char *p; 96 97 if(strncmp(addr, "tcp!", 4) == 0 98 || strncmp(addr, "net!", 4) == 0) 99 addr += 4; 100 addr = estrdup(addr); 101 p = addr+strlen(addr); 102 if(p>addr && *(p-1) == 's') 103 p--; 104 if(p>addr+5 && strcmp(p-5, "!http") == 0) 105 p[-5] = 0; 106 return addr; 107 } 108 109 void 110 wwwauthenticate(HttpState *hs, char *line) 111 { 112 char cred[64], *user, *pass, *realm, *s, *spec, *name; 113 Fmt fmt; 114 UserPasswd *up; 115 116 spec = nil; 117 up = nil; 118 cred[0] = 0; 119 hs->autherror[0] = 0; 120 if(cistrncmp(line, "basic ", 6) != 0){ 121 werrstr("unknown auth: %s", line); 122 goto error; 123 } 124 line += 6; 125 if(cistrncmp(line, "realm=", 6) != 0){ 126 werrstr("missing realm: %s", line); 127 goto error; 128 } 129 line += 6; 130 user = hs->c->url->user; 131 pass = hs->c->url->passwd; 132 if(user==nil || pass==nil){ 133 realm = unquote(line, &line); 134 fmtstrinit(&fmt); 135 name = servername(hs->netaddr); 136 fmtprint(&fmt, "proto=pass service=http server=%q realm=%q", name, realm); 137 free(name); 138 if(hs->c->url->user) 139 fmtprint(&fmt, " user=%q", hs->c->url->user); 140 spec = fmtstrflush(&fmt); 141 if(spec == nil) 142 goto error; 143 if((up = auth_getuserpasswd(nil, "%s", spec)) == nil) 144 goto error; 145 user = up->user; 146 pass = up->passwd; 147 } 148 if((s = smprint("%s:%s", user, pass)) == nil) 149 goto error; 150 free(up); 151 enc64(cred, sizeof(cred), (uchar*)s, strlen(s)); 152 memset(s, 0, strlen(s)); 153 free(s); 154 hs->credentials = smprint("Basic %s", cred); 155 if(hs->credentials == nil) 156 goto error; 157 return; 158 159 error: 160 free(up); 161 free(spec); 162 snprint(hs->autherror, sizeof hs->autherror, "%r"); 163 fprint(2, "%s: Authentication failed: %r\n", argv0); 164 } 165 166 struct { 167 char *name; /* Case-insensitive */ 168 void (*fn)(HttpState *hs, char *value); 169 } hdrtab[] = { 170 { "location:", location }, 171 { "content-type:", contenttype }, 172 { "set-cookie:", setcookie }, 173 { "www-authenticate:", wwwauthenticate }, 174 }; 175 176 static int 177 httprcode(HttpState *hs) 178 { 179 int n; 180 char *p; 181 char buf[256]; 182 183 n = readline(&hs->b, buf, sizeof(buf)-1); 184 if(n <= 0) 185 return n; 186 if(httpdebug) 187 fprint(2, "-> %s\n", buf); 188 p = strchr(buf, ' '); 189 if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){ 190 werrstr("bad response from server"); 191 return -1; 192 } 193 buf[n] = 0; 194 return atoi(p+1); 195 } 196 197 /* 198 * read a single mime header, collect continuations. 199 * 200 * this routine assumes that there is a blank line twixt 201 * the header and the message body, otherwise bytes will 202 * be lost. 203 */ 204 static int 205 getheader(HttpState *hs, char *buf, int n) 206 { 207 char *p, *e; 208 int i; 209 210 n--; 211 p = buf; 212 for(e = p + n; ; p += i){ 213 i = readline(&hs->b, p, e-p); 214 if(i < 0) 215 return i; 216 217 if(p == buf){ 218 /* first line */ 219 if(strchr(buf, ':') == nil) 220 break; /* end of headers */ 221 } else { 222 /* continuation line */ 223 if(*p != ' ' && *p != '\t'){ 224 unreadline(&hs->b, p); 225 *p = 0; 226 break; /* end of this header */ 227 } 228 } 229 } 230 231 if(httpdebug) 232 fprint(2, "-> %s\n", buf); 233 return p-buf; 234 } 235 236 static int 237 httpheaders(HttpState *hs) 238 { 239 char buf[2048]; 240 char *p; 241 int i, n; 242 243 for(;;){ 244 n = getheader(hs, buf, sizeof(buf)); 245 if(n < 0) 246 return -1; 247 if(n == 0) 248 return 0; 249 // print("http header: '%.*s'\n", n, buf); 250 for(i = 0; i < nelem(hdrtab); i++){ 251 n = strlen(hdrtab[i].name); 252 if(cistrncmp(buf, hdrtab[i].name, n) == 0){ 253 /* skip field name and leading white */ 254 p = buf + n; 255 while(*p == ' ' || *p == '\t') 256 p++; 257 (*hdrtab[i].fn)(hs, p); 258 break; 259 } 260 } 261 } 262 } 263 264 int 265 httpopen(Client *c, Url *url) 266 { 267 int fd, code, redirect, authenticate; 268 char *cookies; 269 Ioproc *io; 270 HttpState *hs; 271 char *service; 272 273 if(httpdebug) 274 fprint(2, "httpopen\n"); 275 io = c->io; 276 hs = emalloc(sizeof(*hs)); 277 hs->c = c; 278 279 if(url->port) 280 service = url->port; 281 else 282 service = url->scheme; 283 hs->netaddr = estrdup(netmkaddr(url->host, 0, service)); 284 c->aux = hs; 285 if(httpdebug){ 286 fprint(2, "dial %s\n", hs->netaddr); 287 fprint(2, "dial port: %s\n", url->port); 288 } 289 fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps); 290 if(fd < 0){ 291 Error: 292 if(httpdebug) 293 fprint(2, "iodial: %r\n"); 294 free(hs->location); 295 free(hs->setcookie); 296 free(hs->netaddr); 297 free(hs->credentials); 298 if(fd >= 0) 299 ioclose(io, hs->fd); 300 hs->fd = -1; 301 free(hs); 302 c->aux = nil; 303 return -1; 304 } 305 hs->fd = fd; 306 if(httpdebug) 307 fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n", 308 c->havepostbody? "POST": "GET", url->http.page_spec, url->host); 309 ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n", 310 c->havepostbody? "POST" : "GET", url->http.page_spec, url->host); 311 if(httpdebug) 312 fprint(2, "<- User-Agent: %s\n", c->ctl.useragent); 313 if(c->ctl.useragent) 314 ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent); 315 if(c->ctl.sendcookies){ 316 /* should we use url->page here? sometimes it is nil. */ 317 cookies = httpcookies(url->host, url->http.page_spec, 318 url->ischeme == UShttps); 319 if(cookies && cookies[0]) 320 ioprint(io, fd, "%s", cookies); 321 if(httpdebug) 322 fprint(2, "<- %s", cookies); 323 free(cookies); 324 } 325 if(c->havepostbody){ 326 ioprint(io, fd, "Content-type: %s\r\n", PostContentType); 327 ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody); 328 if(httpdebug){ 329 fprint(2, "<- Content-type: %s\n", PostContentType); 330 fprint(2, "<- Content-length: %ud\n", c->npostbody); 331 } 332 } 333 if(c->authenticate){ 334 ioprint(io, fd, "Authorization: %s\r\n", c->authenticate); 335 if(httpdebug) 336 fprint(2, "<- Authorization: %s\n", c->authenticate); 337 } 338 ioprint(io, fd, "\r\n"); 339 if(c->havepostbody) 340 if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody) 341 goto Error; 342 343 redirect = 0; 344 authenticate = 0; 345 initibuf(&hs->b, io, fd); 346 code = httprcode(hs); 347 348 switch(code){ 349 case -1: /* connection timed out */ 350 goto Error; 351 352 /* 353 case Eof: 354 werrstr("EOF from HTTP server"); 355 goto Error; 356 */ 357 358 case 200: /* OK */ 359 case 201: /* Created */ 360 case 202: /* Accepted */ 361 case 204: /* No Content */ 362 case 205: /* Reset Content */ 363 #ifdef NOT_DEFINED 364 if(ofile == nil && r->start != 0) 365 sysfatal("page changed underfoot"); 366 #endif 367 break; 368 369 case 206: /* Partial Content */ 370 werrstr("Partial Content (206)"); 371 goto Error; 372 373 case 301: /* Moved Permanently */ 374 case 302: /* Moved Temporarily */ 375 case 303: /* See Other */ 376 case 307: /* Temporary Redirect */ 377 redirect = 1; 378 break; 379 380 case 304: /* Not Modified */ 381 break; 382 383 case 400: /* Bad Request */ 384 werrstr("Bad Request (400)"); 385 goto Error; 386 387 case 401: /* Unauthorized */ 388 if(c->authenticate){ 389 werrstr("Authentication failed (401)"); 390 goto Error; 391 } 392 authenticate = 1; 393 break; 394 case 402: /* Payment Required */ 395 werrstr("Payment Required (402)"); 396 goto Error; 397 398 case 403: /* Forbidden */ 399 werrstr("Forbidden by server (403)"); 400 goto Error; 401 402 case 404: /* Not Found */ 403 werrstr("Not found on server (404)"); 404 goto Error; 405 406 case 405: /* Method Not Allowed */ 407 werrstr("Method not allowed (405)"); 408 goto Error; 409 410 case 406: /* Not Acceptable */ 411 werrstr("Not Acceptable (406)"); 412 goto Error; 413 414 case 407: /* Proxy auth */ 415 werrstr("Proxy authentication required (407)"); 416 goto Error; 417 418 case 408: /* Request Timeout */ 419 werrstr("Request Timeout (408)"); 420 goto Error; 421 422 case 409: /* Conflict */ 423 werrstr("Conflict (409)"); 424 goto Error; 425 426 case 410: /* Gone */ 427 werrstr("Gone (410)"); 428 goto Error; 429 430 case 411: /* Length Required */ 431 werrstr("Length Required (411)"); 432 goto Error; 433 434 case 412: /* Precondition Failed */ 435 werrstr("Precondition Failed (412)"); 436 goto Error; 437 438 case 413: /* Request Entity Too Large */ 439 werrstr("Request Entity Too Large (413)"); 440 goto Error; 441 442 case 414: /* Request-URI Too Long */ 443 werrstr("Request-URI Too Long (414)"); 444 goto Error; 445 446 case 415: /* Unsupported Media Type */ 447 werrstr("Unsupported Media Type (415)"); 448 goto Error; 449 450 case 416: /* Requested Range Not Satisfiable */ 451 werrstr("Requested Range Not Satisfiable (416)"); 452 goto Error; 453 454 case 417: /* Expectation Failed */ 455 werrstr("Expectation Failed (417)"); 456 goto Error; 457 458 case 500: /* Internal server error */ 459 werrstr("Server choked (500)"); 460 goto Error; 461 462 case 501: /* Not implemented */ 463 werrstr("Server can't do it (501)"); 464 goto Error; 465 466 case 502: /* Bad gateway */ 467 werrstr("Bad gateway (502)"); 468 goto Error; 469 470 case 503: /* Service unavailable */ 471 werrstr("Service unavailable (503)"); 472 goto Error; 473 474 default: 475 /* Bogus: we should treat unknown code XYZ as code X00 */ 476 werrstr("Unknown response code %d", code); 477 goto Error; 478 } 479 480 if(httpheaders(hs) < 0) 481 goto Error; 482 if(c->ctl.acceptcookies && hs->setcookie) 483 httpsetcookie(hs->setcookie, url->host, url->path); 484 if(authenticate){ 485 if(!hs->credentials){ 486 if(hs->autherror[0]) 487 werrstr("%s", hs->autherror); 488 else 489 werrstr("unauthorized; no www-authenticate: header"); 490 goto Error; 491 } 492 c->authenticate = hs->credentials; 493 hs->credentials = nil; 494 } 495 if(redirect){ 496 if(!hs->location){ 497 werrstr("redirection without Location: header"); 498 goto Error; 499 } 500 c->redirect = hs->location; 501 hs->location = nil; 502 } 503 return 0; 504 } 505 506 int 507 httpread(Client *c, Req *r) 508 { 509 HttpState *hs; 510 long n; 511 512 hs = c->aux; 513 n = readibuf(&hs->b, r->ofcall.data, r->ifcall.count); 514 if(n < 0) 515 return -1; 516 517 r->ofcall.count = n; 518 return 0; 519 } 520 521 void 522 httpclose(Client *c) 523 { 524 HttpState *hs; 525 526 hs = c->aux; 527 if(hs == nil) 528 return; 529 if(hs->fd >= 0) 530 ioclose(c->io, hs->fd); 531 hs->fd = -1; 532 free(hs->location); 533 free(hs->setcookie); 534 free(hs->netaddr); 535 free(hs->credentials); 536 free(hs); 537 c->aux = nil; 538 } 539