1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "httpd.h"
5 #include "httpsrv.h"
6
7 enum
8 {
9 HASHSIZE = 1019,
10 };
11
12 typedef struct Redir Redir;
13 struct Redir
14 {
15 Redir *next;
16 char *pat;
17 char *repl;
18 uint flags; /* generated from repl's decorations */
19 };
20
21 static Redir *redirtab[HASHSIZE];
22 static Redir *vhosttab[HASHSIZE];
23 static char emptystring[1];
24 /* these two arrays must be kept in sync */
25 static char decorations[] = { Modsilent, Modperm, Modsubord, Modonly, '\0' };
26 static uint redirflags[] = { Redirsilent, Redirperm, Redirsubord, Redironly, };
27
28 /* replacement field decorated with redirection modifiers? */
29 static int
isdecorated(char * repl)30 isdecorated(char *repl)
31 {
32 return strchr(decorations, repl[0]) != nil;
33 }
34
35 static uint
decor2flags(char * repl)36 decor2flags(char *repl)
37 {
38 uint flags;
39 char *p;
40
41 flags = 0;
42 while ((p = strchr(decorations, *repl++)) != nil)
43 flags |= redirflags[p - decorations];
44 return flags;
45 }
46
47 /* return replacement without redirection modifiers */
48 char *
undecorated(char * repl)49 undecorated(char *repl)
50 {
51 while (isdecorated(repl))
52 repl++;
53 return repl;
54 }
55
56 static int
hashasu(char * key,int n)57 hashasu(char *key, int n)
58 {
59 ulong h;
60
61 h = 0;
62 while(*key != 0)
63 h = 65599*h + *(uchar*)key++;
64 return h % n;
65 }
66
67 static void
insert(Redir ** tab,char * pat,char * repl)68 insert(Redir **tab, char *pat, char *repl)
69 {
70 Redir **l;
71 Redir *srch;
72 ulong hash;
73
74 hash = hashasu(pat, HASHSIZE);
75 for(l = &tab[hash]; *l; l = &(*l)->next)
76 ;
77 *l = srch = ezalloc(sizeof(Redir));
78 srch->pat = pat;
79 srch->flags = decor2flags(repl);
80 srch->repl = undecorated(repl);
81 srch->next = 0;
82 }
83
84 static void
cleartab(Redir ** tab)85 cleartab(Redir **tab)
86 {
87 Redir *t;
88 int i;
89
90 for(i = 0; i < HASHSIZE; i++){
91 while((t = tab[i]) != nil){
92 tab[i] = t->next;
93 free(t->pat);
94 free(t->repl);
95 free(t);
96 }
97 }
98 }
99
100 void
redirectinit(void)101 redirectinit(void)
102 {
103 static Biobuf *b = nil;
104 static Qid qid;
105 char *file, *line, *s, *host, *field[3];
106 static char pfx[] = "http://";
107
108 file = "/sys/lib/httpd.rewrite";
109 if(b != nil){
110 if(updateQid(Bfildes(b), &qid) == 0)
111 return;
112 Bterm(b);
113 }
114 b = Bopen(file, OREAD);
115 if(b == nil)
116 sysfatal("can't read from %s", file);
117 updateQid(Bfildes(b), &qid);
118
119 cleartab(redirtab);
120 cleartab(vhosttab);
121
122 while((line = Brdline(b, '\n')) != nil){
123 line[Blinelen(b)-1] = 0;
124 s = strchr(line, '#');
125 if(s != nil && (s == line || s[-1] == ' ' || s[-1] == '\t'))
126 *s = '\0'; /* chop comment iff after whitespace */
127 if(tokenize(line, field, nelem(field)) == 2){
128 if(strncmp(field[0], pfx, STRLEN(pfx)) == 0 &&
129 strncmp(undecorated(field[1]), pfx, STRLEN(pfx)) != 0){
130 /* url -> filename */
131 host = field[0] + STRLEN(pfx);
132 s = strrchr(host, '/');
133 if(s)
134 *s = 0; /* chop trailing slash */
135
136 insert(vhosttab, estrdup(host), estrdup(field[1]));
137 }else{
138 insert(redirtab, estrdup(field[0]), estrdup(field[1]));
139 }
140 }
141 }
142 syslog(0, HTTPLOG, "redirectinit pid=%d", getpid());
143 }
144
145 static Redir*
lookup(Redir ** tab,char * pat,int count)146 lookup(Redir **tab, char *pat, int count)
147 {
148 Redir *srch;
149 ulong hash;
150
151 hash = hashasu(pat,HASHSIZE);
152 for(srch = tab[hash]; srch != nil; srch = srch->next)
153 if(strcmp(pat, srch->pat) == 0) {
154 /* only exact match wanted? */
155 if (!(srch->flags & Redironly) || count == 0)
156 return srch;
157 }
158 return nil;
159 }
160
161 static char*
prevslash(char * p,char * s)162 prevslash(char *p, char *s)
163 {
164 while(--s > p)
165 if(*s == '/')
166 break;
167 return s;
168 }
169
170 /*
171 * find the longest match of path against the redirection table,
172 * chopping off the rightmost path component until success or
173 * there's nothing left. return a copy of the replacement string
174 * concatenated with a slash and the portion of the path *not* matched.
175 * So a match of /who/gre/some/stuff.html matched against
176 * /who/gre http://gremlinsrus.org
177 * returns
178 * http://gremlinsrus.org/some/stuff.html
179 *
180 * further flags: if Redironly, match only the named page and no
181 * subordinate ones. if Redirsubord, map the named patch and any
182 * subordinate ones to the same replacement URL.
183 */
184 char*
redirect(HConnect * hc,char * path,uint * flagp)185 redirect(HConnect *hc, char *path, uint *flagp)
186 {
187 Redir *redir;
188 char *s, *newpath, *repl;
189 int c, n, count;
190
191 count = 0;
192 for(s = strchr(path, '\0'); s > path; s = prevslash(path, s)){
193 c = *s;
194 *s = '\0';
195 redir = lookup(redirtab, path, count++);
196 *s = c;
197 if(redir != nil){
198 if (flagp)
199 *flagp = redir->flags;
200 repl = redir->repl;
201 if(redir->flags & Redirsubord)
202 /* don't append s, all matches map to repl */
203 s = "";
204 n = strlen(repl) + strlen(s) + 2 + UTFmax;
205 newpath = halloc(hc, n);
206 snprint(newpath, n, "%s%s", repl, s);
207 return newpath;
208 }
209 }
210 return nil;
211 }
212
213 /*
214 * if host is virtual, return implicit prefix for URI within webroot.
215 * if not, return empty string.
216 * return value should not be freed by caller.
217 */
218 char*
masquerade(char * host)219 masquerade(char *host)
220 {
221 Redir *redir;
222
223 redir = lookup(vhosttab, host, 0);
224 if(redir == nil)
225 return emptystring;
226 return redir->repl;
227 }
228