change some parameters in the library to meet the needs of the website httpbin.org
Fork of MiniTLS-GPL by
tls/tls_socket.c@3:eb324ffffd2b, 2014-06-10 (annotated)
- Committer:
- MiniTLS
- Date:
- Tue Jun 10 14:22:36 2014 +0000
- Revision:
- 3:eb324ffffd2b
- Parent:
- 2:527a66d0a1a9
Fixes for mbed
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
MiniTLS | 2:527a66d0a1a9 | 1 | /* |
MiniTLS | 2:527a66d0a1a9 | 2 | MiniTLS - A super trimmed down TLS/SSL Library for embedded devices |
MiniTLS | 2:527a66d0a1a9 | 3 | Author: Donatien Garnier |
MiniTLS | 2:527a66d0a1a9 | 4 | Copyright (C) 2013-2014 AppNearMe Ltd |
MiniTLS | 2:527a66d0a1a9 | 5 | |
MiniTLS | 2:527a66d0a1a9 | 6 | This program is free software; you can redistribute it and/or |
MiniTLS | 2:527a66d0a1a9 | 7 | modify it under the terms of the GNU General Public License |
MiniTLS | 2:527a66d0a1a9 | 8 | as published by the Free Software Foundation; either version 2 |
MiniTLS | 2:527a66d0a1a9 | 9 | of the License, or (at your option) any later version. |
MiniTLS | 2:527a66d0a1a9 | 10 | |
MiniTLS | 2:527a66d0a1a9 | 11 | This program is distributed in the hope that it will be useful, |
MiniTLS | 2:527a66d0a1a9 | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
MiniTLS | 2:527a66d0a1a9 | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
MiniTLS | 2:527a66d0a1a9 | 14 | GNU General Public License for more details. |
MiniTLS | 2:527a66d0a1a9 | 15 | |
MiniTLS | 2:527a66d0a1a9 | 16 | You should have received a copy of the GNU General Public License |
MiniTLS | 2:527a66d0a1a9 | 17 | along with this program; if not, write to the Free Software |
MiniTLS | 2:527a66d0a1a9 | 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
MiniTLS | 2:527a66d0a1a9 | 19 | *//** |
MiniTLS | 2:527a66d0a1a9 | 20 | * \file tls_socket.c |
MiniTLS | 2:527a66d0a1a9 | 21 | * \copyright Copyright (c) AppNearMe Ltd 2013 |
MiniTLS | 2:527a66d0a1a9 | 22 | * \author Donatien Garnier |
MiniTLS | 2:527a66d0a1a9 | 23 | */ |
MiniTLS | 2:527a66d0a1a9 | 24 | |
MiniTLS | 3:eb324ffffd2b | 25 | #define __DEBUG__ 4 |
MiniTLS | 2:527a66d0a1a9 | 26 | #ifndef __MODULE__ |
MiniTLS | 2:527a66d0a1a9 | 27 | #define __MODULE__ "tls_socket.c" |
MiniTLS | 2:527a66d0a1a9 | 28 | #endif |
MiniTLS | 2:527a66d0a1a9 | 29 | |
MiniTLS | 2:527a66d0a1a9 | 30 | |
MiniTLS | 2:527a66d0a1a9 | 31 | #include "core/fwk.h" |
MiniTLS | 2:527a66d0a1a9 | 32 | #include "inc/minitls_errors.h" |
MiniTLS | 2:527a66d0a1a9 | 33 | |
MiniTLS | 2:527a66d0a1a9 | 34 | #include "tls_socket.h" |
MiniTLS | 2:527a66d0a1a9 | 35 | |
MiniTLS | 2:527a66d0a1a9 | 36 | minitls_err_t tls_socket_init(tls_socket_t* socket, minitls_t* minitls, |
MiniTLS | 2:527a66d0a1a9 | 37 | uint8_t* write_buffer, size_t write_buffer_size, uint8_t* read_buffer, size_t read_buffer_size) |
MiniTLS | 2:527a66d0a1a9 | 38 | { |
MiniTLS | 2:527a66d0a1a9 | 39 | socket->minitls = minitls; |
MiniTLS | 2:527a66d0a1a9 | 40 | |
MiniTLS | 2:527a66d0a1a9 | 41 | socket->events = NULL; |
MiniTLS | 2:527a66d0a1a9 | 42 | |
MiniTLS | 2:527a66d0a1a9 | 43 | socket->read_buffer = NULL; |
MiniTLS | 2:527a66d0a1a9 | 44 | buffer_init(&socket->write_buffer, write_buffer, write_buffer_size); |
MiniTLS | 2:527a66d0a1a9 | 45 | |
MiniTLS | 2:527a66d0a1a9 | 46 | socket->session.session_id_length = 0; |
MiniTLS | 2:527a66d0a1a9 | 47 | |
MiniTLS | 2:527a66d0a1a9 | 48 | minitls_err_t ret = tls_record_init(&socket->record, socket, read_buffer, read_buffer_size); |
MiniTLS | 2:527a66d0a1a9 | 49 | if(ret) |
MiniTLS | 2:527a66d0a1a9 | 50 | { |
MiniTLS | 2:527a66d0a1a9 | 51 | return ret; |
MiniTLS | 2:527a66d0a1a9 | 52 | } |
MiniTLS | 2:527a66d0a1a9 | 53 | |
MiniTLS | 2:527a66d0a1a9 | 54 | ret = tls_handshake_init(&socket->handshake, socket); |
MiniTLS | 2:527a66d0a1a9 | 55 | if(ret) |
MiniTLS | 2:527a66d0a1a9 | 56 | { |
MiniTLS | 2:527a66d0a1a9 | 57 | return ret; |
MiniTLS | 2:527a66d0a1a9 | 58 | } |
MiniTLS | 2:527a66d0a1a9 | 59 | |
MiniTLS | 2:527a66d0a1a9 | 60 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 61 | } |
MiniTLS | 2:527a66d0a1a9 | 62 | |
MiniTLS | 2:527a66d0a1a9 | 63 | minitls_err_t tls_socket_connect(tls_socket_t* socket, const char* hostname, uint16_t port, int timeout) |
MiniTLS | 2:527a66d0a1a9 | 64 | { |
MiniTLS | 2:527a66d0a1a9 | 65 | minitls_err_t ret = tls_record_connect(&socket->record, hostname, port); |
MiniTLS | 2:527a66d0a1a9 | 66 | if(ret) |
MiniTLS | 2:527a66d0a1a9 | 67 | { |
MiniTLS | 2:527a66d0a1a9 | 68 | ERR("Could not connect"); |
MiniTLS | 2:527a66d0a1a9 | 69 | return ret; |
MiniTLS | 2:527a66d0a1a9 | 70 | } |
MiniTLS | 2:527a66d0a1a9 | 71 | |
MiniTLS | 2:527a66d0a1a9 | 72 | //TODO we could allocate the handshake structure here to save memory |
MiniTLS | 2:527a66d0a1a9 | 73 | |
MiniTLS | 2:527a66d0a1a9 | 74 | ret = tls_handshake_start(&socket->handshake); |
MiniTLS | 2:527a66d0a1a9 | 75 | if(ret) |
MiniTLS | 2:527a66d0a1a9 | 76 | { |
MiniTLS | 2:527a66d0a1a9 | 77 | ERR("Could not start handshake"); |
MiniTLS | 2:527a66d0a1a9 | 78 | return ret; |
MiniTLS | 2:527a66d0a1a9 | 79 | } |
MiniTLS | 2:527a66d0a1a9 | 80 | |
MiniTLS | 2:527a66d0a1a9 | 81 | //Now wait for handshake to finish processing (or fail) |
MiniTLS | 2:527a66d0a1a9 | 82 | while(!tls_handshake_is_done(&socket->handshake)) |
MiniTLS | 2:527a66d0a1a9 | 83 | { |
MiniTLS | 2:527a66d0a1a9 | 84 | DBG("Handshaking..."); |
MiniTLS | 2:527a66d0a1a9 | 85 | ret = tls_record_process(&socket->record); |
MiniTLS | 2:527a66d0a1a9 | 86 | if(ret) |
MiniTLS | 2:527a66d0a1a9 | 87 | { |
MiniTLS | 2:527a66d0a1a9 | 88 | ERR("Processing error"); |
MiniTLS | 2:527a66d0a1a9 | 89 | return ret; |
MiniTLS | 2:527a66d0a1a9 | 90 | } |
MiniTLS | 2:527a66d0a1a9 | 91 | } |
MiniTLS | 2:527a66d0a1a9 | 92 | |
MiniTLS | 2:527a66d0a1a9 | 93 | DBG("Connected"); |
MiniTLS | 2:527a66d0a1a9 | 94 | |
MiniTLS | 2:527a66d0a1a9 | 95 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 96 | } |
MiniTLS | 2:527a66d0a1a9 | 97 | |
MiniTLS | 2:527a66d0a1a9 | 98 | void tls_socket_copy_session(tls_socket_t* to, tls_socket_t* from) |
MiniTLS | 2:527a66d0a1a9 | 99 | { |
MiniTLS | 2:527a66d0a1a9 | 100 | memcpy(&to->session, &from->session, sizeof(tls_session_t)); |
MiniTLS | 2:527a66d0a1a9 | 101 | } |
MiniTLS | 2:527a66d0a1a9 | 102 | |
MiniTLS | 2:527a66d0a1a9 | 103 | //Events API -- select like |
MiniTLS | 2:527a66d0a1a9 | 104 | minitls_err_t tls_socket_event_list_init(tls_socket_event_list_t* list) |
MiniTLS | 2:527a66d0a1a9 | 105 | { |
MiniTLS | 2:527a66d0a1a9 | 106 | list->head = NULL; |
MiniTLS | 2:527a66d0a1a9 | 107 | |
MiniTLS | 2:527a66d0a1a9 | 108 | //Create semaphore |
MiniTLS | 2:527a66d0a1a9 | 109 | /* list->sem = rtos_sem_create(1, 0);*/ |
MiniTLS | 2:527a66d0a1a9 | 110 | /*if(list->sem == NULL) |
MiniTLS | 2:527a66d0a1a9 | 111 | { |
MiniTLS | 2:527a66d0a1a9 | 112 | return TLS_ERR_MEMORY; |
MiniTLS | 2:527a66d0a1a9 | 113 | }*/ //FIXME |
MiniTLS | 2:527a66d0a1a9 | 114 | |
MiniTLS | 2:527a66d0a1a9 | 115 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 116 | } |
MiniTLS | 2:527a66d0a1a9 | 117 | |
MiniTLS | 2:527a66d0a1a9 | 118 | minitls_err_t tls_socket_event_init_and_register(tls_socket_event_t* event, tls_socket_event_list_t* list, tls_socket_t* socket, bool read, bool write) |
MiniTLS | 2:527a66d0a1a9 | 119 | { |
MiniTLS | 2:527a66d0a1a9 | 120 | //Init event |
MiniTLS | 2:527a66d0a1a9 | 121 | event->socket = socket; |
MiniTLS | 2:527a66d0a1a9 | 122 | event->read = read; |
MiniTLS | 2:527a66d0a1a9 | 123 | event->write = write; |
MiniTLS | 2:527a66d0a1a9 | 124 | event->list = list; |
MiniTLS | 2:527a66d0a1a9 | 125 | event->socket_list_next = NULL; |
MiniTLS | 2:527a66d0a1a9 | 126 | event->event_list_next = NULL; |
MiniTLS | 2:527a66d0a1a9 | 127 | |
MiniTLS | 2:527a66d0a1a9 | 128 | //Add to select list (tail) |
MiniTLS | 2:527a66d0a1a9 | 129 | if(list->head != NULL) |
MiniTLS | 2:527a66d0a1a9 | 130 | { |
MiniTLS | 2:527a66d0a1a9 | 131 | tls_socket_event_t* previous_event = list->head; |
MiniTLS | 2:527a66d0a1a9 | 132 | while( previous_event->event_list_next != NULL ) |
MiniTLS | 2:527a66d0a1a9 | 133 | { |
MiniTLS | 2:527a66d0a1a9 | 134 | previous_event = previous_event->event_list_next; |
MiniTLS | 2:527a66d0a1a9 | 135 | } |
MiniTLS | 2:527a66d0a1a9 | 136 | previous_event->event_list_next = event; |
MiniTLS | 2:527a66d0a1a9 | 137 | } |
MiniTLS | 2:527a66d0a1a9 | 138 | else |
MiniTLS | 2:527a66d0a1a9 | 139 | { |
MiniTLS | 2:527a66d0a1a9 | 140 | list->head = event; |
MiniTLS | 2:527a66d0a1a9 | 141 | } |
MiniTLS | 2:527a66d0a1a9 | 142 | |
MiniTLS | 2:527a66d0a1a9 | 143 | //Add to socket's event list (tail) |
MiniTLS | 2:527a66d0a1a9 | 144 | if(socket->events != NULL) |
MiniTLS | 2:527a66d0a1a9 | 145 | { |
MiniTLS | 2:527a66d0a1a9 | 146 | tls_socket_event_t* previous_event = socket->events; |
MiniTLS | 2:527a66d0a1a9 | 147 | while( previous_event->socket_list_next != NULL ) |
MiniTLS | 2:527a66d0a1a9 | 148 | { |
MiniTLS | 2:527a66d0a1a9 | 149 | previous_event = previous_event->socket_list_next; |
MiniTLS | 2:527a66d0a1a9 | 150 | } |
MiniTLS | 2:527a66d0a1a9 | 151 | previous_event->socket_list_next = event; |
MiniTLS | 2:527a66d0a1a9 | 152 | } |
MiniTLS | 2:527a66d0a1a9 | 153 | else |
MiniTLS | 2:527a66d0a1a9 | 154 | { |
MiniTLS | 2:527a66d0a1a9 | 155 | socket->events = event; |
MiniTLS | 2:527a66d0a1a9 | 156 | } |
MiniTLS | 2:527a66d0a1a9 | 157 | |
MiniTLS | 2:527a66d0a1a9 | 158 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 159 | } |
MiniTLS | 2:527a66d0a1a9 | 160 | |
MiniTLS | 2:527a66d0a1a9 | 161 | minitls_err_t tls_socket_event_list_wait(tls_socket_event_list_t* list, int timeout) //All events unregistered at the end of wait, timeout in ms, -1 for infinite timeout |
MiniTLS | 2:527a66d0a1a9 | 162 | {/* |
MiniTLS | 2:527a66d0a1a9 | 163 | if( rtos_sem_get(list->sem, timeout) ) //FIXME |
MiniTLS | 2:527a66d0a1a9 | 164 | {*/ |
MiniTLS | 2:527a66d0a1a9 | 165 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 166 | /*} |
MiniTLS | 2:527a66d0a1a9 | 167 | else |
MiniTLS | 2:527a66d0a1a9 | 168 | { |
MiniTLS | 2:527a66d0a1a9 | 169 | return TLS_ERR_TIMEOUT; |
MiniTLS | 2:527a66d0a1a9 | 170 | }*/ |
MiniTLS | 2:527a66d0a1a9 | 171 | } |
MiniTLS | 2:527a66d0a1a9 | 172 | |
MiniTLS | 2:527a66d0a1a9 | 173 | //These calls are non-blocking |
MiniTLS | 2:527a66d0a1a9 | 174 | minitls_err_t tls_socket_read(tls_socket_t* socket, uint8_t* bytes, size_t max_size, size_t* read_size) |
MiniTLS | 2:527a66d0a1a9 | 175 | { |
MiniTLS | 2:527a66d0a1a9 | 176 | DBG("Reading at most %d bytes", max_size); |
MiniTLS | 2:527a66d0a1a9 | 177 | if(socket->read_buffer == NULL) //Socket is not ready |
MiniTLS | 2:527a66d0a1a9 | 178 | { |
MiniTLS | 2:527a66d0a1a9 | 179 | WARN("Socket is not ready or has been closed"); |
MiniTLS | 2:527a66d0a1a9 | 180 | *read_size = 0; |
MiniTLS | 2:527a66d0a1a9 | 181 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 182 | } |
MiniTLS | 2:527a66d0a1a9 | 183 | |
MiniTLS | 2:527a66d0a1a9 | 184 | size_t length = buffer_length(socket->read_buffer); |
MiniTLS | 2:527a66d0a1a9 | 185 | *read_size = MIN(max_size, length); |
MiniTLS | 2:527a66d0a1a9 | 186 | |
MiniTLS | 2:527a66d0a1a9 | 187 | buffer_nbytes_read(socket->read_buffer, bytes, *read_size); |
MiniTLS | 2:527a66d0a1a9 | 188 | |
MiniTLS | 2:527a66d0a1a9 | 189 | DBG("%d bytes read", *read_size); |
MiniTLS | 2:527a66d0a1a9 | 190 | |
MiniTLS | 2:527a66d0a1a9 | 191 | //If buffer has been fully read, invalidate read_buffer |
MiniTLS | 2:527a66d0a1a9 | 192 | if( buffer_length(socket->read_buffer) == 0 ) |
MiniTLS | 2:527a66d0a1a9 | 193 | { |
MiniTLS | 2:527a66d0a1a9 | 194 | socket->read_buffer = NULL; |
MiniTLS | 2:527a66d0a1a9 | 195 | } |
MiniTLS | 2:527a66d0a1a9 | 196 | |
MiniTLS | 2:527a66d0a1a9 | 197 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 198 | } |
MiniTLS | 2:527a66d0a1a9 | 199 | |
MiniTLS | 2:527a66d0a1a9 | 200 | minitls_err_t tls_socket_write(tls_socket_t* socket, uint8_t* bytes, size_t size, size_t* written_size) |
MiniTLS | 2:527a66d0a1a9 | 201 | { |
MiniTLS | 2:527a66d0a1a9 | 202 | size_t length = buffer_space(&socket->write_buffer); |
MiniTLS | 2:527a66d0a1a9 | 203 | *written_size = MIN(size, length); |
MiniTLS | 2:527a66d0a1a9 | 204 | |
MiniTLS | 2:527a66d0a1a9 | 205 | buffer_nbytes_write(&socket->write_buffer, bytes, *written_size); |
MiniTLS | 2:527a66d0a1a9 | 206 | |
MiniTLS | 2:527a66d0a1a9 | 207 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 208 | } |
MiniTLS | 2:527a66d0a1a9 | 209 | |
MiniTLS | 2:527a66d0a1a9 | 210 | minitls_err_t tls_socket_flush_read(tls_socket_t* socket, int timeout) |
MiniTLS | 2:527a66d0a1a9 | 211 | { |
MiniTLS | 2:527a66d0a1a9 | 212 | DBG("Get new bytes"); |
MiniTLS | 2:527a66d0a1a9 | 213 | if( socket->read_buffer != NULL ) //No need to read more bytes |
MiniTLS | 2:527a66d0a1a9 | 214 | { |
MiniTLS | 2:527a66d0a1a9 | 215 | WARN("Buffer is already non-empty"); |
MiniTLS | 2:527a66d0a1a9 | 216 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 217 | } |
MiniTLS | 2:527a66d0a1a9 | 218 | |
MiniTLS | 2:527a66d0a1a9 | 219 | tls_record_set_read_timeout(&socket->record, timeout); |
MiniTLS | 2:527a66d0a1a9 | 220 | |
MiniTLS | 2:527a66d0a1a9 | 221 | int ret; |
MiniTLS | 2:527a66d0a1a9 | 222 | do |
MiniTLS | 2:527a66d0a1a9 | 223 | { |
MiniTLS | 2:527a66d0a1a9 | 224 | DBG("Processing TLS packet"); |
MiniTLS | 2:527a66d0a1a9 | 225 | ret = tls_record_process(&socket->record); |
MiniTLS | 2:527a66d0a1a9 | 226 | if(ret) |
MiniTLS | 2:527a66d0a1a9 | 227 | { |
MiniTLS | 2:527a66d0a1a9 | 228 | return ret; |
MiniTLS | 2:527a66d0a1a9 | 229 | } |
MiniTLS | 2:527a66d0a1a9 | 230 | } while( socket->read_buffer == NULL ); |
MiniTLS | 2:527a66d0a1a9 | 231 | |
MiniTLS | 2:527a66d0a1a9 | 232 | DBG("Buffer has been filled"); |
MiniTLS | 2:527a66d0a1a9 | 233 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 234 | } |
MiniTLS | 2:527a66d0a1a9 | 235 | |
MiniTLS | 2:527a66d0a1a9 | 236 | minitls_err_t tls_socket_flush_write(tls_socket_t* socket, int timeout) |
MiniTLS | 2:527a66d0a1a9 | 237 | { |
MiniTLS | 2:527a66d0a1a9 | 238 | if(buffer_length(&socket->write_buffer) == 0) |
MiniTLS | 2:527a66d0a1a9 | 239 | { |
MiniTLS | 2:527a66d0a1a9 | 240 | return MINITLS_OK; //Useless to flush |
MiniTLS | 2:527a66d0a1a9 | 241 | } |
MiniTLS | 2:527a66d0a1a9 | 242 | |
MiniTLS | 2:527a66d0a1a9 | 243 | tls_record_set_write_timeout(&socket->record, timeout); |
MiniTLS | 2:527a66d0a1a9 | 244 | |
MiniTLS | 2:527a66d0a1a9 | 245 | //Pass buffer to record layer |
MiniTLS | 2:527a66d0a1a9 | 246 | minitls_err_t ret = tls_record_send(&socket->record, TLS_APPLICATION_DATA, &socket->write_buffer); |
MiniTLS | 2:527a66d0a1a9 | 247 | |
MiniTLS | 2:527a66d0a1a9 | 248 | //TODO: Advertise writeable here |
MiniTLS | 2:527a66d0a1a9 | 249 | |
MiniTLS | 2:527a66d0a1a9 | 250 | if(ret) |
MiniTLS | 2:527a66d0a1a9 | 251 | { |
MiniTLS | 2:527a66d0a1a9 | 252 | return ret; |
MiniTLS | 2:527a66d0a1a9 | 253 | } |
MiniTLS | 2:527a66d0a1a9 | 254 | |
MiniTLS | 2:527a66d0a1a9 | 255 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 256 | } |
MiniTLS | 2:527a66d0a1a9 | 257 | |
MiniTLS | 2:527a66d0a1a9 | 258 | //Called from record layer |
MiniTLS | 2:527a66d0a1a9 | 259 | minitls_err_t tls_socket_readable_callback(tls_socket_t* socket, buffer_t* buffer) |
MiniTLS | 2:527a66d0a1a9 | 260 | { |
MiniTLS | 2:527a66d0a1a9 | 261 | socket->read_buffer = buffer; |
MiniTLS | 2:527a66d0a1a9 | 262 | |
MiniTLS | 2:527a66d0a1a9 | 263 | //Process events |
MiniTLS | 2:527a66d0a1a9 | 264 | |
MiniTLS | 2:527a66d0a1a9 | 265 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 266 | } |
MiniTLS | 2:527a66d0a1a9 | 267 | |
MiniTLS | 2:527a66d0a1a9 | 268 | minitls_err_t tls_socket_close(tls_socket_t* socket) |
MiniTLS | 2:527a66d0a1a9 | 269 | { |
MiniTLS | 2:527a66d0a1a9 | 270 | minitls_err_t ret = tls_record_close(&socket->record); |
MiniTLS | 2:527a66d0a1a9 | 271 | if(ret) |
MiniTLS | 2:527a66d0a1a9 | 272 | { |
MiniTLS | 2:527a66d0a1a9 | 273 | return ret; |
MiniTLS | 2:527a66d0a1a9 | 274 | } |
MiniTLS | 2:527a66d0a1a9 | 275 | return MINITLS_OK; |
MiniTLS | 2:527a66d0a1a9 | 276 | } |