/src/libwebsockets/lib/roles/http/date.c
Line | Count | Source (jump to first uncovered line) |
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 |
38 | | lws_http_date_render(char *buf, size_t len, const struct tm *tm) |
39 | 0 | { |
40 | 0 | const char *w = s + 36 + (3 * tm->tm_wday), *m = s + (3 * tm->tm_mon); |
41 | |
|
42 | 0 | if (len < 29) |
43 | 0 | return -1; |
44 | | |
45 | 0 | lws_snprintf(buf, len, "%c%c%c, %02d %c%c%c %d %02d:%02d:%02d GMT", |
46 | 0 | w[0], w[1], w[2], tm->tm_mday, m[0], m[1], m[2], |
47 | 0 | 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); |
48 | |
|
49 | 0 | return 0; |
50 | 0 | } |
51 | | |
52 | | |
53 | | int |
54 | | lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t) |
55 | 0 | { |
56 | 0 | #if defined(LWS_HAVE_GMTIME_R) |
57 | 0 | struct tm tmp; |
58 | 0 | struct tm *tm = gmtime_r(t, &tmp); |
59 | | #else |
60 | | struct tm *tm = gmtime(t); |
61 | | #endif |
62 | 0 | if (!tm) |
63 | 0 | return -1; |
64 | | |
65 | 0 | if (lws_http_date_render(buf, len, tm)) |
66 | 0 | return -1; |
67 | | |
68 | 0 | return 0; |
69 | 0 | } |
70 | | |
71 | | static int |
72 | | lws_http_date_parse(const char *b, size_t len, struct tm *tm) |
73 | 0 | { |
74 | 0 | int n; |
75 | |
|
76 | 0 | if (len < 29) |
77 | 0 | 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 | 0 | if (b[3] != ',' || b[4] != ' ' || b[7] != ' ' || b[11] != ' ' || |
86 | 0 | b[16] != ' ' || b[19] != ':' || b[22] != ':' || b[25] != ' ' || |
87 | 0 | b[26] != 'G' || b[27] != 'M' || b[28] != 'T') |
88 | 0 | return -1; |
89 | | |
90 | 0 | memset(tm, 0, sizeof(*tm)); |
91 | |
|
92 | 0 | for (n = 36; n < 57; n += 3) |
93 | 0 | if (b[0] == s[n] && b[1] == s[n + 1] && b[2] == s[n + 2]) |
94 | 0 | break; |
95 | 0 | else |
96 | 0 | tm->tm_wday++; |
97 | |
|
98 | 0 | if (n == 57) |
99 | 0 | return -1; |
100 | | |
101 | 0 | for (n = 0; n < 36; n += 3) |
102 | 0 | if (b[8] == s[n] && b[9] == s[n + 1] && b[10] == s[n + 2]) |
103 | 0 | break; |
104 | 0 | else |
105 | 0 | tm->tm_mon++; |
106 | |
|
107 | 0 | if (n == 36) |
108 | 0 | return -1; |
109 | | |
110 | 0 | tm->tm_mday = atoi(b + 5); |
111 | 0 | n = atoi(b + 12); |
112 | 0 | if (n < 1900) |
113 | 0 | return -1; |
114 | 0 | tm->tm_year = n - 1900; |
115 | |
|
116 | 0 | n = atoi(b + 17); |
117 | 0 | if (n < 0 || n > 23) |
118 | 0 | return -1; |
119 | 0 | tm->tm_hour = n; |
120 | |
|
121 | 0 | n = atoi(b + 20); |
122 | 0 | if (n < 0 || n > 60) |
123 | 0 | return -1; |
124 | 0 | tm->tm_min = n; |
125 | |
|
126 | 0 | n = atoi(b + 23); |
127 | 0 | if (n < 0 || n > 61) /* leap second */ |
128 | 0 | return -1; |
129 | 0 | tm->tm_sec = n; |
130 | |
|
131 | 0 | return 0; |
132 | 0 | } |
133 | | |
134 | | int |
135 | | lws_http_date_parse_unix(const char *b, size_t len, time_t *t) |
136 | 0 | { |
137 | 0 | struct tm tm; |
138 | |
|
139 | 0 | if (lws_http_date_parse(b, len, &tm)) |
140 | 0 | return -1; |
141 | | |
142 | | #if defined(WIN32) |
143 | | *t = _mkgmtime(&tm); |
144 | | #else |
145 | 0 | #if defined(LWS_HAVE_TIMEGM) |
146 | 0 | *t = timegm(&tm); |
147 | | #else |
148 | | /* this is a poor fallback since it uses localtime zone */ |
149 | | *t = mktime(&tm); |
150 | | #endif |
151 | 0 | #endif |
152 | |
|
153 | 0 | return (int)*t == -1 ? -1 : 0; |
154 | 0 | } |
155 | | |
156 | | #if defined(LWS_WITH_CLIENT) |
157 | | |
158 | | int |
159 | | lws_http_check_retry_after(struct lws *wsi, lws_usec_t *us_interval_in_out) |
160 | 0 | { |
161 | 0 | size_t len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_RETRY_AFTER); |
162 | 0 | char *p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_RETRY_AFTER); |
163 | 0 | lws_usec_t u; |
164 | 0 | time_t t, td; |
165 | |
|
166 | 0 | if (!p) |
167 | 0 | 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 | 0 | if (len < 9) |
179 | 0 | u = ((lws_usec_t)(time_t)atoi(p)) * LWS_USEC_PER_SEC; |
180 | 0 | else { |
181 | |
|
182 | 0 | if (lws_http_date_parse_unix(p, len, &t)) |
183 | 0 | 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 | 0 | time(&td); |
192 | 0 | len = (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_DATE); |
193 | 0 | if (len) { |
194 | 0 | p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_DATE); |
195 | | /* if this fails, it leaves td as client time */ |
196 | 0 | (void)lws_http_date_parse_unix(p, len, &td); |
197 | 0 | } |
198 | |
|
199 | 0 | 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 | 0 | return 1; |
206 | | |
207 | 0 | u = ((lws_usec_t)(t - td)) * LWS_USEC_PER_SEC; |
208 | 0 | } |
209 | | |
210 | | /* |
211 | | * We are only willing to increase the incoming interval, not |
212 | | * decrease it |
213 | | */ |
214 | | |
215 | 0 | if (u < *us_interval_in_out) |
216 | | /* keep the incoming interval */ |
217 | 0 | return 1; |
218 | | |
219 | | /* use the computed interval */ |
220 | 0 | *us_interval_in_out = u; |
221 | |
|
222 | 0 | return 0; |
223 | 0 | } |
224 | | |
225 | | #endif |