• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-mcu / 1642556678

27 Jan 2025 01:30PM UTC coverage: 25.749%. First build
1642556678

push

gitlab-ci

vpodzime
feat: Support platform-specific or custom allocators

And use Zephyr-specific allocation functions on Zephyr by
default, using either a separate heap (default) or the system
heap.

This means we have to use our memory management functions instead
of the standard ones everywhere, even if they can actually just
call the standard ones.

Ticket: MEN-7808
Changelog: none
Signed-off-by: Vratislav Podzimek <vratislav.podzimek@northern.tech>

64 of 184 new or added lines in 12 files covered. (34.78%)

731 of 2839 relevant lines covered (25.75%)

8.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/platform/net/generic/curl/src/mender-http.c
1
/**
2
 * @file      mender-http.c
3
 * @brief     Mender HTTP interface for curl platform, requires libcurl >= 7.80.0
4
 *
5
 * Copyright joelguittet and mender-mcu-client contributors
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19

20
#include <curl/curl.h>
21
#include "mender-alloc.h"
22
#include "mender-http.h"
23
#include "mender-log.h"
24
#include "mender-utils.h"
25

26
/**
27
 * @brief HTTP User-Agent
28
 */
29
#ifdef MENDER_CLIENT_VERSION
30
#define MENDER_HTTP_USER_AGENT "mender-mcu-client/" MENDER_CLIENT_VERSION " (mender-http) curl/" LIBCURL_VERSION
31
#else
32
#define MENDER_HTTP_USER_AGENT "mender-mcu-client (mender-http) curl/" LIBCURL_VERSION
33
#endif /* MENDER_CLIENT_VERSION */
34

35
/* CURL_MAX_WRITE_SIZE is the maximum amount of data curl passes to the
36
   WRITEFUNCTION (see below) according to man:CURLOPT_WRITEFUNCTION(3). */
37
const size_t mender_http_recv_buf_length = CURL_MAX_WRITE_SIZE;
38

39
/**
40
 * @brief User data
41
 */
42
typedef struct {
43
    mender_err_t (*callback)(mender_http_client_event_t, void *, size_t, void *); /**< Callback invoked on HTTP events */
44
    void *params;                                                                 /**< Parameters passed to the callback, NULL if not used */
45
} mender_http_curl_user_data_t;
46

47
/**
48
 * @brief Mender HTTP configuration
49
 */
50
static mender_http_config_t http_config;
51

52
/**
53
 * @brief HTTP PREREQ callback, used to inform the client is connected to the server
54
 * @param params User data
55
 * @param conn_primary_ip Primary IP of the remote server 
56
 * @param conn_local_ip Originating IP of the connection
57
 * @param conn_primary_port Primary port number on the remote server
58
 * @param conn_local_port Originating port number of the connection
59
 * @return CURL_PREREQFUNC_OK if the function succeeds, CURL_PREREQFUNC_ABORT otherwise
60
 */
61
static int http_prereq_callback(void *params, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port);
62

63
/**
64
 * @brief HTTP write callback, used to retrieve data from the server
65
 * @param data Data from the server
66
 * @param size Size of the data
67
 * @param nmemb Number of element
68
 * @param params User data
69
 * @return Real size of data if the function succeeds, -1 otherwise
70
 */
71
static size_t http_write_callback(char *data, size_t size, size_t nmemb, void *params);
72

73
/**
74
 * @brief HTTP PREREQ callback for artifact
75
 * @see http_prereq_callback()
76
 */
77
static int artifact_prereq_callback(void *user_data, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port);
78

79
/**
80
 * @brief HTTP write callback for artifact
81
 * @see http_write_callback()
82
 */
83
static size_t artifact_write_callback(char *data, size_t size, size_t nmemb, void *user_data);
84

85
mender_err_t
86
mender_http_init(mender_http_config_t *config) {
×
87

88
    assert(NULL != config);
×
89
    assert(NULL != config->host);
×
90

91
    /* Save configuration */
92
    memcpy(&http_config, config, sizeof(mender_http_config_t));
×
93

94
    /* Initialization of curl */
95
    curl_global_init(CURL_GLOBAL_DEFAULT);
×
96

97
    return MENDER_OK;
×
98
}
99

100
mender_err_t
101
mender_http_perform(char                *jwt,
×
102
                    char                *path,
103
                    mender_http_method_t method,
104
                    char                *payload,
105
                    char                *signature,
106
                    mender_err_t (*callback)(mender_http_client_event_t, void *, size_t, void *),
107
                    void *params,
108
                    int  *status) {
109

110
    assert(NULL != path);
×
111
    assert(NULL != callback);
×
112
    assert(NULL != status);
×
113
    CURLcode           err;
114
    mender_err_t       ret             = MENDER_OK;
×
115
    CURL              *curl            = NULL;
×
116
    char              *url             = NULL;
×
117
    char              *bearer          = NULL;
×
118
    char              *x_men_signature = NULL;
×
119
    struct curl_slist *headers         = NULL;
×
120

121
    /* Compute URL if required */
122
    if (!mender_utils_strbeginswith(path, "http://") && !mender_utils_strbeginswith(path, "https://")) {
×
NEW
123
        if (-1 == mender_utils_asprintf(&url, "%s%s", http_config.host, path)) {
×
124
            mender_log_error("Unable to allocate memory for URL");
×
125
            ret = MENDER_FAIL;
×
126
            goto END;
×
127
        }
128
    }
129

130
    /* Initialization of the client */
131
    if (NULL == (curl = curl_easy_init())) {
×
132
        mender_log_error("Unable to allocate memory");
×
133
        ret = MENDER_FAIL;
×
134
        goto END;
×
135
    }
136

137
    /* Configuration of the client */
138
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_URL, (NULL != url) ? url : path))) {
×
139
        mender_log_error("Unable to set HTTP URL: %s", curl_easy_strerror(err));
×
140
        ret = MENDER_FAIL;
×
141
        goto END;
×
142
    }
143
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_USERAGENT, MENDER_HTTP_USER_AGENT))) {
×
144
        mender_log_error("Unable to set HTTP User-Agent: %s", curl_easy_strerror(err));
×
145
        ret = MENDER_FAIL;
×
146
        goto END;
×
147
    }
148
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2))) {
×
149
        mender_log_error("Unable to set TLSv1.2: %s", curl_easy_strerror(err));
×
150
        ret = MENDER_FAIL;
×
151
        goto END;
×
152
    }
153
    mender_http_curl_user_data_t user_data = { .callback = callback, .params = params };
×
154
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_PREREQFUNCTION, &http_prereq_callback))) {
×
155
        mender_log_error("Unable to set HTTP PREREQ function: %s", curl_easy_strerror(err));
×
156
        ret = MENDER_FAIL;
×
157
        goto END;
×
158
    }
159
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_PREREQDATA, &user_data))) {
×
160
        mender_log_error("Unable to set HTTP PREREQ data: %s", curl_easy_strerror(err));
×
161
        ret = MENDER_FAIL;
×
162
        goto END;
×
163
    }
164
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &http_write_callback))) {
×
165
        mender_log_error("Unable to set HTTP write function: %s", curl_easy_strerror(err));
×
166
        ret = MENDER_FAIL;
×
167
        goto END;
×
168
    }
169
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &user_data))) {
×
170
        mender_log_error("Unable to set HTTP write data: %s", curl_easy_strerror(err));
×
171
        ret = MENDER_FAIL;
×
172
        goto END;
×
173
    }
174
    if (NULL != jwt) {
×
175
        size_t str_length = strlen("Authorization: Bearer ") + strlen(jwt) + 1;
×
NEW
176
        if (NULL == (bearer = (char *)mender_malloc(str_length))) {
×
177
            mender_log_error("Unable to allocate memory");
×
178
            ret = MENDER_FAIL;
×
179
            goto END;
×
180
        }
181
        snprintf(bearer, str_length, "Authorization: Bearer %s", jwt);
×
182
        headers = curl_slist_append(headers, bearer);
×
183
    }
184
    if (NULL != signature) {
×
185
        size_t str_length = strlen("X-MEN-Signature: ") + strlen(signature) + 1;
×
NEW
186
        if (NULL == (x_men_signature = (char *)mender_malloc(str_length))) {
×
187
            mender_log_error("Unable to allocate memory");
×
188
            ret = MENDER_FAIL;
×
189
            goto END;
×
190
        }
191
        snprintf(x_men_signature, str_length, "X-MEN-Signature: %s", signature);
×
192
        headers = curl_slist_append(headers, x_men_signature);
×
193
    }
194
    if (NULL != payload) {
×
195
        headers = curl_slist_append(headers, "Content-Type: application/json");
×
196
    }
197
    if (NULL != headers) {
×
198
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
×
199
    }
200

201
    /* Write data if payload is defined */
202
    if (NULL != payload) {
×
203
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
×
204
        if (MENDER_HTTP_PUT == method) {
×
205
            curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
×
206
        }
207
    }
208

209
    /* Perform request */
210
    if (CURLE_OK != (err = curl_easy_perform(curl))) {
×
211
        mender_log_error("Unable to perform HTTP request: %s", curl_easy_strerror(err));
×
212
        callback(MENDER_HTTP_EVENT_ERROR, NULL, 0, params);
×
213
        ret = MENDER_FAIL;
×
214
        goto END;
×
215
    }
216

217
    /* Read HTTP status code */
218
    long response_code;
219
    if (CURLE_OK != (err = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code))) {
×
220
        mender_log_error("Unable to read HTTP response code: %s", curl_easy_strerror(err));
×
221
        ret = MENDER_FAIL;
×
222
        goto END;
×
223
    }
224
    *status = (int)response_code;
×
225
    if (MENDER_OK != (ret = callback(MENDER_HTTP_EVENT_DISCONNECTED, NULL, 0, params))) {
×
226
        mender_log_error("An error occurred when processing HTTP disconnection");
×
227
        goto END;
×
228
    }
229

230
END:
×
231

232
    /* Release memory */
233
    curl_easy_cleanup(curl);
×
234
    curl_slist_free_all(headers);
×
NEW
235
    mender_free(x_men_signature);
×
NEW
236
    mender_free(bearer);
×
NEW
237
    mender_free(url);
×
238

239
    return ret;
×
240
}
241

242
mender_err_t
243
mender_http_artifact_download(const char *uri, mender_artifact_download_data_t *dl_data, int *status) {
×
244
    assert(NULL != uri);
×
245
    assert(NULL != dl_data);
×
246
    assert(NULL != status);
×
247

248
    CURLcode           err;
249
    mender_err_t       ret     = MENDER_OK;
×
250
    CURL              *curl    = NULL;
×
251
    char              *url     = NULL;
×
252
    struct curl_slist *headers = NULL;
×
253

254
    /* Compute URL if required */
255
    if (!mender_utils_strbeginswith(uri, "http://") && !mender_utils_strbeginswith(uri, "https://")) {
×
NEW
256
        if (-1 == mender_utils_asprintf(&url, "%s%s", http_config.host, uri)) {
×
257
            mender_log_error("Unable to allocate memory");
×
258
            ret = MENDER_FAIL;
×
259
            goto END;
×
260
        }
261
    }
262

263
    /* Initialization of the client */
264
    if (NULL == (curl = curl_easy_init())) {
×
265
        mender_log_error("Unable to allocate memory");
×
266
        ret = MENDER_FAIL;
×
267
        goto END;
×
268
    }
269

270
    /* Configuration of the client */
271
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_URL, (NULL != url) ? url : uri))) {
×
272
        mender_log_error("Unable to set HTTP URL: %s", curl_easy_strerror(err));
×
273
        ret = MENDER_FAIL;
×
274
        goto END;
×
275
    }
276

277
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2))) {
×
278
        mender_log_error("Unable to set TLSv1.2: %s", curl_easy_strerror(err));
×
279
        ret = MENDER_FAIL;
×
280
        goto END;
×
281
    }
282

283
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_PREREQFUNCTION, &artifact_prereq_callback))) {
×
284
        mender_log_error("Unable to set HTTP PREREQ function: %s", curl_easy_strerror(err));
×
285
        ret = MENDER_FAIL;
×
286
        goto END;
×
287
    }
288
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_PREREQDATA, dl_data))) {
×
289
        mender_log_error("Unable to set HTTP PREREQ data: %s", curl_easy_strerror(err));
×
290
        ret = MENDER_FAIL;
×
291
        goto END;
×
292
    }
293
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &artifact_write_callback))) {
×
294
        mender_log_error("Unable to set HTTP write function: %s", curl_easy_strerror(err));
×
295
        ret = MENDER_FAIL;
×
296
        goto END;
×
297
    }
298
    if (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEDATA, dl_data))) {
×
299
        mender_log_error("Unable to set HTTP write data: %s", curl_easy_strerror(err));
×
300
        ret = MENDER_FAIL;
×
301
        goto END;
×
302
    }
303

304
    /* Perform request */
305
    if (CURLE_OK != (err = curl_easy_perform(curl))) {
×
306
        mender_log_error("Unable to perform HTTP request: %s", curl_easy_strerror(err));
×
307
        dl_data->artifact_download_callback(MENDER_HTTP_EVENT_ERROR, NULL, 0, dl_data);
×
308
        ret = MENDER_FAIL;
×
309
        goto END;
×
310
    }
311

312
    /* Artifact download failed*/
313
    if (MENDER_OK != dl_data->ret) {
×
314
        ret = MENDER_FAIL;
×
315
        goto END;
×
316
    }
317

318
    /* Read HTTP status code */
319
    long response_code;
320
    if (CURLE_OK != (err = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code))) {
×
321
        mender_log_error("Unable to read HTTP response code: %s", curl_easy_strerror(err));
×
322
        ret = MENDER_FAIL;
×
323
        goto END;
×
324
    }
325
    *status = (int)response_code;
×
326
    if (MENDER_OK != (ret = dl_data->artifact_download_callback(MENDER_HTTP_EVENT_DISCONNECTED, NULL, 0, dl_data))) {
×
327
        mender_log_error("An error occurred when processing disconnection in artifact download");
×
328
        goto END;
×
329
    }
330

331
END:
×
332

333
    /* Release memory */
334
    curl_easy_cleanup(curl);
×
335
    curl_slist_free_all(headers);
×
NEW
336
    mender_free(url);
×
337

338
    return ret;
×
339
}
340

341
mender_err_t
342
mender_http_exit(void) {
×
343

344
    /* Cleaning */
345
    curl_global_cleanup();
×
346

347
    return MENDER_OK;
×
348
}
349

350
static int
351
http_prereq_callback(void                   *params,
×
352
                     MENDER_ARG_UNUSED char *conn_primary_ip,
353
                     MENDER_ARG_UNUSED char *conn_local_ip,
354
                     MENDER_ARG_UNUSED int   conn_primary_port,
355
                     MENDER_ARG_UNUSED int   conn_local_port) {
356

357
    assert(NULL != params);
×
358
    mender_http_curl_user_data_t *user_data = (mender_http_curl_user_data_t *)params;
×
359

360
    /* Invoke callback */
361
    if (MENDER_OK != user_data->callback(MENDER_HTTP_EVENT_CONNECTED, NULL, 0, user_data->params)) {
×
362
        mender_log_error("An error occurred when processing HTTP connection");
×
363
        return CURL_PREREQFUNC_ABORT;
×
364
    }
365

366
    return CURL_PREREQFUNC_OK;
×
367
}
368

369
static size_t
370
http_write_callback(char *data, size_t size, size_t nmemb, void *params) {
×
371

372
    assert(NULL != params);
×
373
    mender_http_curl_user_data_t *user_data = (mender_http_curl_user_data_t *)params;
×
374
    size_t                        realsize  = size * nmemb;
×
375

376
    /* Transmit data received to the upper layer */
377
    if (realsize > 0) {
×
378
        if (MENDER_OK != user_data->callback(MENDER_HTTP_EVENT_DATA_RECEIVED, (void *)data, realsize, user_data->params)) {
×
379
            mender_log_error("An error occurred, stop reading data");
×
380
            return -1;
×
381
        }
382
    }
383

384
    return realsize;
×
385
}
386

387
static int
388
artifact_prereq_callback(void                   *user_data,
×
389
                         MENDER_ARG_UNUSED char *conn_primary_ip,
390
                         MENDER_ARG_UNUSED char *conn_local_ip,
391
                         MENDER_ARG_UNUSED int   conn_primary_port,
392
                         MENDER_ARG_UNUSED int   conn_local_port) {
393
    assert(NULL != user_data);
×
394

395
    mender_artifact_download_data_t *dl_data = user_data;
×
396

397
    /* Invoke callback */
398
    if (MENDER_OK != dl_data->artifact_download_callback(MENDER_HTTP_EVENT_CONNECTED, NULL, 0, dl_data)) {
×
399
        mender_log_error("An error occurred when processing HTTP connection on artifact download");
×
400
        return CURL_PREREQFUNC_ABORT;
×
401
    }
402

403
    return CURL_PREREQFUNC_OK;
×
404
}
405

406
static size_t
407
artifact_write_callback(char *data, size_t size, size_t nmemb, void *user_data) {
×
408
    assert(NULL != user_data);
×
409

410
    mender_artifact_download_data_t *dl_data  = user_data;
×
411
    size_t                           realsize = size * nmemb;
×
412

413
    /* Transmit data received to the upper layer */
414
    if (realsize > 0) {
×
415
        if (MENDER_OK != dl_data->artifact_download_callback(MENDER_HTTP_EVENT_DATA_RECEIVED, (void *)data, realsize, dl_data)) {
×
416
            mender_log_error("An error occurred, stop reading data");
×
417
            return -1;
×
418
        }
419
    }
420

421
    return realsize;
×
422
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc