Coverage Report

Created: 2023-03-26 07:20

/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