1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * RFC7231 date string generation and parsing
25 */
26
27 #include "private-lib-core.h"
28
29 /*
30 * To avoid needless pointers, we encode these in one string using the fact
31 * they're 3 chars each to index it
32 */
33
34 static const char *const s =
35 "JanFebMarAprMayJunJulAugSepOctNovDecMonTueWedThuFriSatSun";
36
37 static int
lws_http_date_render(char * buf,size_t len,const struct tm * tm)38 lws_http_date_render(char *buf, size_t len, const struct tm *tm)
39 {
40 const char *w = s + 36 + (3 * tm->tm_wday), *m = s + (3 * tm->tm_mon);
41
42 if (len < 29)
43 return -1;
44
45 lws_snprintf(buf, len, "%c%c%c, %02d %c%c%c %d %02d:%02d:%02d GMT",
46 w[0], w[1], w[2], tm->tm_mday, m[0], m[1], m[2],
47 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
48
49 return 0;
50 }
51
52
53 int
lws_http_date_render_from_unix(char * buf,size_t len,const time_t * t)54 lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t)
55 {
56 #if defined(LWS_HAVE_GMTIME_R)
57 struct tm tmp;
58 struct tm *tm = gmtime_r(t, &tmp);
59 #else
60 struct tm *tm = gmtime(t);
61 #endif
62 if (!tm)
63 return -1;
64
65 if (lws_http_date_render(buf, len, tm))
66 return -1;
67
68 return 0;
69 }
70
71 static int
lws_http_date_parse(const char * b,size_t len,struct tm * tm)72 lws_http_date_parse(const char *b, size_t len, struct tm *tm)
73 {
74 int n;
75
76 if (len < 29)
77 return -1;
78
79 /*
80 * We reject anything that isn't a properly-formatted RFC7231 date, eg
81 *
82 * Tue, 15 Nov 1994 08:12:31 GMT
83 */
84
85 if (b[3] != ',' || b[4] != ' ' || b[7] != ' ' || b[11] != ' ' ||
86 b[16] != ' ' || b[19] != ':' || b[22] != ':' || b[25] != ' ' ||
87 b[26] != 'G' || b[27] != 'M' || b[28] != 'T')
88 return -1;
89
90 memset(tm, 0, sizeof(*tm));
91
92 for (n = 36; n < 57; n += 3)
93 if (b[0] == s[n] && b[1] == s[n + 1] && b[2] == s[n + 2])
94 break;
95 else
96 tm->tm_wday++;
97
98 if (n == 57)
99 return -1;
100
101 for (n = 0; n < 36; n += 3)
102 if (b[8] == s[n] && b[9] == s[n + 1] && b[10] == s[n + 2])
103 break;
104 else
105 tm->tm_mon++;
106
107 if (n == 36)
108 return -1;
109
110 tm->tm_mday = atoi(b + 5);
111 n = atoi(b + 12);
112 if (n < 1900)
113 return -1;
114 tm->tm_year = n - 1900;
115
116 n = atoi(b + 17);
117 if (n < 0 || n > 23)
118 return -1;
119 tm->tm_hour = n;
120
121 n = atoi(b + 20);
122 if (n < 0 || n > 60)
123 return -1;
124 tm->tm_min = n;
125
126 n = atoi(b + 23);
127 if (n < 0 || n > 61) /* leap second */
128 return -1;
129 tm->tm_sec = n;
130
131 return 0;
132 }
133
134 int
lws_http_date_parse_unix(const char * b,size_t len,time_t * t)135 lws_http_date_parse_unix(const char *b, size_t len, time_t *t)
136 {
137 struct tm tm;
138
139 if (lws_http_date_parse(b, len, &tm))
140 return -1;
141
142 #if defined(WIN32)
143 *t = _mkgmtime(&tm);
144 #else
145 #if defined(LWS_HAVE_TIMEGM)
146 *t = timegm(&tm);
147 #else
148 /* this is a poor fallback since it uses localtime zone */
149 *t = mktime(&tm);
150 #endif
151 #endif
152
153 return (int)*t == -1 ? -1 : 0;
154 }
155
156 #if defined(LWS_WITH_CLIENT)
157
158 int
lws_http_check_retry_after(struct lws * wsi,lws_usec_t * us_interval_in_out)159 lws_http_check_retry_after(struct lws *wsi, lws_usec_t *us_interval_in_out)
160 {
161 size_t len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_RETRY_AFTER);
162 char *p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_RETRY_AFTER);
163 lws_usec_t u;
164 time_t t, td;
165
166 if (!p)
167 return 1;
168
169 /*
170 * There are two arg styles for RETRY_AFTER specified in RFC7231 7.1.3,
171 * either a full absolute second-resolution date/time, or an integer
172 * interval
173 *
174 * Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
175 * Retry-After: 120
176 */
177
178 if (len < 9)
179 u = ((lws_usec_t)(time_t)atoi(p)) * LWS_USEC_PER_SEC;
180 else {
181
182 if (lws_http_date_parse_unix(p, len, &t))
183 return 1;
184
185 /*
186 * If possible, look for DATE from the server as well, so we
187 * can calculate the interval it thinks it is giving us,
188 * eliminating problems from server - client clock skew
189 */
190
191 time(&td);
192 len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_DATE);
193 if (len) {
194 p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_DATE);
195 /* if this fails, it leaves td as client time */
196 (void)lws_http_date_parse_unix(p, len, &td);
197 }
198
199 if (td >= t)
200 /*
201 * if he's effectively giving us a 0 or negative
202 * interval, just ignore the whole thing and keep the
203 * incoming interval
204 */
205 return 1;
206
207 u = ((lws_usec_t)(t - td)) * LWS_USEC_PER_SEC;
208 }
209
210 /*
211 * We are only willing to increase the incoming interval, not
212 * decrease it
213 */
214
215 if (u < *us_interval_in_out)
216 /* keep the incoming interval */
217 return 1;
218
219 /* use the computed interval */
220 *us_interval_in_out = u;
221
222 return 0;
223 }
224
225 #endif
226