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