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/octet-stream"; 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 } 164 165 struct { 166 char *name; /* Case-insensitive */ 167 void (*fn)(HttpState *hs, char *value); 168 } hdrtab[] = { 169 { "location:", location }, 170 { "content-type:", contenttype }, 171 { "set-cookie:", setcookie }, 172 { "www-authenticate:", wwwauthenticate }, 173 }; 174 175 static int 176 httprcode(HttpState *hs) 177 { 178 int n; 179 char *p; 180 char buf[256]; 181 182 n = readline(&hs->b, buf, sizeof(buf)-1); 183 if(n <= 0) 184 return n; 185 if(httpdebug) 186 fprint(2, "-> %s\n", buf); 187 p = strchr(buf, ' '); 188 if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){ 189 werrstr("bad response from server"); 190 return -1; 191 } 192 buf[n] = 0; 193 return atoi(p+1); 194 } 195 196 /* 197 * read a single mime header, collect continuations. 198 * 199 * this routine assumes that there is a blank line twixt 200 * the header and the message body, otherwise bytes will 201 * be lost. 202 */ 203 static int 204 getheader(HttpState *hs, char *buf, int n) 205 { 206 char *p, *e; 207 int i; 208 209 n--; 210 p = buf; 211 for(e = p + n; ; p += i){ 212 i = readline(&hs->b, p, e-p); 213 if(i < 0) 214 return i; 215 216 if(p == buf){ 217 /* first line */ 218 if(strchr(buf, ':') == nil) 219 break; /* end of headers */ 220 } else { 221 /* continuation line */ 222 if(*p != ' ' && *p != '\t'){ 223 unreadline(&hs->b, p); 224 *p = 0; 225 break; /* end of this header */ 226 } 227 } 228 } 229 230 if(httpdebug) 231 fprint(2, "-> %s\n", buf); 232 return p-buf; 233 } 234 235 static int 236 httpheaders(HttpState *hs) 237 { 238 char buf[2048]; 239 char *p; 240 int i, n; 241 242 for(;;){ 243 n = getheader(hs, buf, sizeof(buf)); 244 if(n < 0) 245 return -1; 246 if(n == 0) 247 return 0; 248 // print("http header: '%.*s'\n", n, buf); 249 for(i = 0; i < nelem(hdrtab); i++){ 250 n = strlen(hdrtab[i].name); 251 if(cistrncmp(buf, hdrtab[i].name, n) == 0){ 252 /* skip field name and leading white */ 253 p = buf + n; 254 while(*p == ' ' || *p == '\t') 255 p++; 256 (*hdrtab[i].fn)(hs, p); 257 break; 258 } 259 } 260 } 261 } 262 263 int 264 httpopen(Client *c, Url *url) 265 { 266 int fd, code, redirect, authenticate; 267 char *cookies; 268 Ioproc *io; 269 HttpState *hs; 270 271 if(httpdebug) 272 fprint(2, "httpopen\n"); 273 io = c->io; 274 hs = emalloc(sizeof(*hs)); 275 hs->c = c; 276 hs->netaddr = estrdup(netmkaddr(url->host, 0, url->scheme)); 277 c->aux = hs; 278 if(httpdebug) 279 fprint(2, "dial %s\n", hs->netaddr); 280 fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps); 281 if(fd < 0){ 282 Error: 283 if(httpdebug) 284 fprint(2, "iodial: %r\n"); 285 free(hs->netaddr); 286 close(hs->fd); 287 hs->fd = -1; 288 free(hs); 289 c->aux = nil; 290 return -1; 291 } 292 hs->fd = fd; 293 if(httpdebug) 294 fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n", 295 c->havepostbody? "POST": " GET", url->http.page_spec, url->host); 296 ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n", 297 c->havepostbody? "POST" : "GET", url->http.page_spec, url->host); 298 if(httpdebug) 299 fprint(2, "<- User-Agent: %s\n", c->ctl.useragent); 300 if(c->ctl.useragent) 301 ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent); 302 if(c->ctl.sendcookies){ 303 /* should we use url->page here? sometimes it is nil. */ 304 cookies = httpcookies(url->host, url->http.page_spec, 0); 305 if(cookies && cookies[0]) 306 ioprint(io, fd, "%s", cookies); 307 if(httpdebug) 308 fprint(2, "<- %s", cookies); 309 free(cookies); 310 } 311 if(c->havepostbody){ 312 ioprint(io, fd, "Content-type: %s\r\n", PostContentType); 313 ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody); 314 if(httpdebug){ 315 fprint(2, "<- Content-type: %s\n", PostContentType); 316 fprint(2, "<- Content-length: %ud\n", c->npostbody); 317 } 318 } 319 if(c->authenticate){ 320 ioprint(io, fd, "Authorization: %s\r\n", c->authenticate); 321 if(httpdebug) 322 fprint(2, "<- Authorization: %s\n", c->authenticate); 323 } 324 ioprint(io, fd, "\r\n"); 325 if(c->havepostbody) 326 if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody) 327 goto Error; 328 329 c->havepostbody = 0; 330 redirect = 0; 331 authenticate = 0; 332 initibuf(&hs->b, io, fd); 333 code = httprcode(hs); 334 335 switch(code){ 336 case -1: /* connection timed out */ 337 goto Error; 338 339 /* 340 case Eof: 341 werrstr("EOF from HTTP server"); 342 goto Error; 343 */ 344 345 case 200: /* OK */ 346 case 201: /* Created */ 347 case 202: /* Accepted */ 348 case 204: /* No Content */ 349 #ifdef NOT_DEFINED 350 if(ofile == nil && r->start != 0) 351 sysfatal("page changed underfoot"); 352 #endif 353 break; 354 355 case 206: /* Partial Content */ 356 werrstr("Partial Content (206)"); 357 goto Error; 358 359 case 301: /* Moved Permanently */ 360 case 302: /* Moved Temporarily */ 361 redirect = 1; 362 break; 363 364 case 304: /* Not Modified */ 365 break; 366 367 case 400: /* Bad Request */ 368 werrstr("Bad Request (400)"); 369 goto Error; 370 371 case 401: /* Unauthorized */ 372 if(c->authenticate){ 373 werrstr("Authentication failed (401)"); 374 goto Error; 375 } 376 authenticate = 1; 377 break; 378 case 402: /* ??? */ 379 werrstr("Unauthorized (402)"); 380 goto Error; 381 382 case 403: /* Forbidden */ 383 werrstr("Forbidden by server (403)"); 384 goto Error; 385 386 case 404: /* Not Found */ 387 werrstr("Not found on server (404)"); 388 goto Error; 389 390 case 405: /* Method Not Allowed */ 391 werrstr("Method not allowed (405)"); 392 goto Error; 393 394 case 407: /* Proxy auth */ 395 werrstr("Proxy authentication required (407)"); 396 goto Error; 397 398 case 500: /* Internal server error */ 399 werrstr("Server choked (500)"); 400 goto Error; 401 402 case 501: /* Not implemented */ 403 werrstr("Server can't do it (501)"); 404 goto Error; 405 406 case 502: /* Bad gateway */ 407 werrstr("Bad gateway (502)"); 408 goto Error; 409 410 case 503: /* Service unavailable */ 411 werrstr("Service unavailable (503)"); 412 goto Error; 413 414 default: 415 /* Bogus: we should treat unknown code XYZ as code X00 */ 416 werrstr("Unknown response code %d", code); 417 goto Error; 418 } 419 420 if(httpheaders(hs) < 0) 421 goto Error; 422 if(c->ctl.acceptcookies && hs->setcookie) 423 httpsetcookie(hs->setcookie, url->host, url->path); 424 if(authenticate){ 425 if(!hs->credentials){ 426 if(hs->autherror[0]) 427 werrstr("%s", hs->autherror); 428 else 429 werrstr("unauthorized; no www-authenticate: header"); 430 return -1; 431 } 432 c->authenticate = hs->credentials; 433 hs->credentials = nil; 434 } 435 if(redirect){ 436 if(!hs->location){ 437 werrstr("redirection without Location: header"); 438 return -1; 439 } 440 c->redirect = hs->location; 441 hs->location = nil; 442 } 443 return 0; 444 } 445 446 int 447 httpread(Client *c, Req *r) 448 { 449 char *dst; 450 HttpState *hs; 451 int n; 452 long rlen, tot, len; 453 454 hs = c->aux; 455 dst = r->ofcall.data; 456 len = r->ifcall.count; 457 tot = 0; 458 while (tot < len){ 459 rlen = len - tot; 460 n = readibuf(&hs->b, dst + tot, rlen); 461 if(n == 0) 462 break; 463 else if(n < 0){ 464 if(tot == 0) 465 return -1; 466 else 467 return tot; 468 } 469 tot += n; 470 } 471 r->ofcall.count = tot; 472 return 0; 473 } 474 475 void 476 httpclose(Client *c) 477 { 478 HttpState *hs; 479 480 hs = c->aux; 481 if(hs == nil) 482 return; 483 ioclose(c->io, hs->fd); 484 hs->fd = -1; 485 free(hs->location); 486 free(hs->setcookie); 487 free(hs->netaddr); 488 free(hs->credentials); 489 free(hs); 490 c->aux = nil; 491 } 492 493