1 /*
2 * This file is part of the openHiTLS project.
3 *
4 * openHiTLS is licensed under the Mulan PSL v2.
5 * You can use this software according to the terms and conditions of the Mulan PSL v2.
6 * You may obtain a copy of Mulan PSL v2 at:
7 *
8 * http://license.coscl.org.cn/MulanPSL2
9 *
10 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13 * See the Mulan PSL v2 for more details.
14 */
15
16 #include "hitls_build.h"
17 #ifdef HITLS_BSL_UIO_BUFFER
18
19 #include "securec.h"
20 #include "bsl_sal.h"
21 #include "bsl_errno.h"
22 #include "bsl_err_internal.h"
23 #include "bsl_uio.h"
24 #include "uio_abstraction.h"
25
26 // The write behavior must be the same.
27 #define UIO_BUFFER_DEFAULT_SIZE 4096
28 #define DTLS_MIN_MTU 256 /* Minimum MTU setting size */
29 #define DTLS_MAX_MTU_OVERHEAD 48 /* Highest MTU overhead, IPv6 40 + UDP 8 */
30
31 typedef struct {
32 uint32_t outSize;
33 // This variable will make the write() logic consistent with the ossl. Reason:
34 // 1) The handshake logic is complex.
35 // 2) The behavior consistency problem of the handshake logic is difficult to locate.
36 uint32_t outOff;
37 uint32_t outLen;
38 uint8_t *outBuf;
39 } BufferCtx;
40
BufferCreate(BSL_UIO * uio)41 static int32_t BufferCreate(BSL_UIO *uio)
42 {
43 if (uio == NULL) {
44 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
45 return BSL_NULL_INPUT;
46 }
47 BufferCtx *ctx = BSL_SAL_Calloc(1, sizeof(BufferCtx));
48 if (ctx == NULL) {
49 BSL_ERR_PUSH_ERROR(BSL_MALLOC_FAIL);
50 return BSL_MALLOC_FAIL;
51 }
52 ctx->outSize = UIO_BUFFER_DEFAULT_SIZE;
53 ctx->outBuf = (uint8_t *)BSL_SAL_Malloc(UIO_BUFFER_DEFAULT_SIZE);
54 if (ctx->outBuf == NULL) {
55 BSL_SAL_FREE(ctx);
56 BSL_ERR_PUSH_ERROR(BSL_MALLOC_FAIL);
57 return BSL_MALLOC_FAIL;
58 }
59 BSL_UIO_SetCtx(uio, ctx);
60 uio->init = 1;
61 return BSL_SUCCESS;
62 }
63
BufferDestroy(BSL_UIO * uio)64 static int32_t BufferDestroy(BSL_UIO *uio)
65 {
66 if (uio == NULL) {
67 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
68 return BSL_NULL_INPUT;
69 }
70 BufferCtx *ctx = BSL_UIO_GetCtx(uio);
71 if (ctx != NULL) {
72 BSL_SAL_FREE(ctx->outBuf);
73 BSL_SAL_FREE(ctx);
74 BSL_UIO_SetCtx(uio, NULL);
75 }
76 uio->flags = 0;
77 uio->init = 0;
78 return BSL_SUCCESS;
79 }
80
BufferFlushInternal(BSL_UIO * uio)81 static int32_t BufferFlushInternal(BSL_UIO *uio)
82 {
83 BufferCtx *ctx = BSL_UIO_GetCtx(uio);
84 if (ctx == NULL) {
85 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
86 return BSL_NULL_INPUT;
87 }
88 while (ctx->outLen > 0) {
89 uint32_t tmpWriteLen = 0;
90 int32_t ret = BSL_UIO_Write(uio->next, &ctx->outBuf[ctx->outOff], ctx->outLen, &tmpWriteLen);
91 if (ret != BSL_SUCCESS) {
92 uio->flags = uio->next->flags;
93 return ret;
94 }
95 if (tmpWriteLen == 0) {
96 BSL_ERR_PUSH_ERROR(BSL_UIO_IO_BUSY);
97 return BSL_UIO_IO_BUSY;
98 }
99 ctx->outOff += tmpWriteLen;
100 ctx->outLen -= tmpWriteLen;
101 }
102 ctx->outOff = 0;
103 ctx->outLen = 0;
104 return BSL_SUCCESS;
105 }
106
BufferFlush(BSL_UIO * uio,int32_t larg,void * parg)107 static int32_t BufferFlush(BSL_UIO *uio, int32_t larg, void *parg)
108 {
109 bool invalid = (uio == NULL) || (uio->next == NULL) || (uio->ctx == NULL);
110 if (invalid) {
111 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
112 return BSL_NULL_INPUT;
113 }
114 BufferCtx *ctx = BSL_UIO_GetCtx(uio);
115 if (ctx->outLen == 0) { // invoke the flush of the next UIO object
116 return BSL_UIO_Ctrl(uio->next, BSL_UIO_FLUSH, larg, parg);
117 }
118 (void)BSL_UIO_ClearFlags(uio, (BSL_UIO_FLAGS_RWS | BSL_UIO_FLAGS_SHOULD_RETRY));
119 int32_t ret = BufferFlushInternal(uio);
120 if (ret != BSL_SUCCESS) {
121 BSL_ERR_PUSH_ERROR(ret);
122 return ret;
123 }
124 return BSL_UIO_Ctrl(uio->next, BSL_UIO_FLUSH, larg, parg);
125 }
126
BufferReset(BSL_UIO * uio)127 static int32_t BufferReset(BSL_UIO *uio)
128 {
129 if (uio == NULL || uio->ctx == NULL) {
130 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
131 return BSL_NULL_INPUT;
132 }
133 BufferCtx *ctx = uio->ctx;
134 ctx->outLen = 0;
135 ctx->outOff = 0;
136
137 if (uio->next == NULL) {
138 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
139 return BSL_NULL_INPUT;
140 }
141 return BSL_UIO_Ctrl(uio->next, BSL_UIO_RESET, 0, NULL);
142 }
143
BufferSetBufferSize(BSL_UIO * uio,int32_t larg,void * parg)144 static int32_t BufferSetBufferSize(BSL_UIO *uio, int32_t larg, void *parg)
145 {
146 if (larg != (int32_t)sizeof(uint32_t) || parg == NULL || *(uint32_t *)parg < DTLS_MIN_MTU - DTLS_MAX_MTU_OVERHEAD) {
147 BSL_ERR_PUSH_ERROR(BSL_INVALID_ARG);
148 return BSL_INVALID_ARG;
149 }
150 BufferCtx *ctx = BSL_UIO_GetCtx(uio);
151 if (ctx == NULL) {
152 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
153 return BSL_NULL_INPUT;
154 }
155 uint32_t len = *(uint32_t *)parg;
156 BSL_SAL_FREE(ctx->outBuf);
157 ctx->outBuf = (uint8_t *)BSL_SAL_Malloc(len);
158 if (ctx->outBuf == NULL) {
159 BSL_ERR_PUSH_ERROR(BSL_MALLOC_FAIL);
160 return BSL_MALLOC_FAIL;
161 }
162 ctx->outSize = len;
163 return BSL_SUCCESS;
164 }
165
BufferCtrl(BSL_UIO * uio,int32_t cmd,int32_t larg,void * parg)166 static int32_t BufferCtrl(BSL_UIO *uio, int32_t cmd, int32_t larg, void *parg)
167 {
168 switch (cmd) {
169 case BSL_UIO_FLUSH:
170 return BufferFlush(uio, larg, parg);
171 case BSL_UIO_RESET:
172 return BufferReset(uio);
173 case BSL_UIO_SET_BUFFER_SIZE:
174 return BufferSetBufferSize(uio, larg, parg);
175 default:
176 if (uio->next != NULL) {
177 return BSL_UIO_Ctrl(uio->next, cmd, larg, parg);
178 }
179 break;
180 }
181 BSL_ERR_PUSH_ERROR(BSL_UIO_FAIL);
182 return BSL_UIO_FAIL;
183 }
184
185 // Add data to the remaining space.
TryCompleteBuffer(BufferCtx * ctx,const void * in,uint32_t remain,uint32_t * writeLen)186 static int32_t TryCompleteBuffer(BufferCtx *ctx, const void *in, uint32_t remain, uint32_t *writeLen)
187 {
188 const uint32_t freeSpace = ctx->outSize - (ctx->outOff + ctx->outLen);
189 if (freeSpace == 0) {
190 return BSL_SUCCESS;
191 }
192 const uint32_t real = (freeSpace < remain) ? freeSpace : remain;
193 if (memcpy_s(&ctx->outBuf[ctx->outOff + ctx->outLen], freeSpace, in, real) != EOK) {
194 BSL_ERR_PUSH_ERROR(BSL_UIO_IO_EXCEPTION);
195 return BSL_UIO_IO_EXCEPTION;
196 }
197 ctx->outLen += real;
198 *writeLen += real;
199 return BSL_SUCCESS;
200 }
201
BufferWrite(BSL_UIO * uio,const void * buf,uint32_t len,uint32_t * writeLen)202 static int32_t BufferWrite(BSL_UIO *uio, const void *buf, uint32_t len, uint32_t *writeLen)
203 {
204 bool invalid = (uio == NULL) || (buf == NULL) || (writeLen == NULL) || (uio->next == NULL);
205 if (invalid) {
206 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
207 return BSL_NULL_INPUT;
208 }
209 *writeLen = 0;
210 BufferCtx *ctx = BSL_UIO_GetCtx(uio);
211 invalid = (ctx == NULL) || (ctx->outBuf == NULL);
212 if (invalid) {
213 BSL_ERR_PUSH_ERROR(BSL_NULL_INPUT);
214 return BSL_NULL_INPUT;
215 }
216 (void)BSL_UIO_ClearFlags(uio, (BSL_UIO_FLAGS_RWS | BSL_UIO_FLAGS_SHOULD_RETRY));
217 const uint8_t *in = buf;
218 uint32_t remain = len;
219 while (remain > 0) {
220 const uint32_t freeSpace = ctx->outSize - (ctx->outOff + ctx->outLen);
221 if (freeSpace >= remain) { // If the space is sufficient, cache the data.
222 return TryCompleteBuffer(ctx, in, remain, writeLen);
223 }
224 // else: space is insufficient
225 if (ctx->outLen > 0) { // buffer already has data, need to send the existing data first.
226 int32_t ret = BufferFlushInternal(uio);
227 if (ret != BSL_SUCCESS) {
228 return ret;
229 }
230 }
231 ctx->outOff = 0;
232 while (remain >= ctx->outSize) {
233 uint32_t tmpWriteLen = 0;
234 int32_t ret = BSL_UIO_Write(uio->next, in, remain, &tmpWriteLen);
235 if (ret != BSL_SUCCESS) {
236 uio->flags = uio->next->flags;
237 return ret;
238 }
239 *writeLen += tmpWriteLen;
240 in = &in[tmpWriteLen];
241 remain -= tmpWriteLen;
242 }
243 }
244 return BSL_SUCCESS;
245 }
246
BSL_UIO_BufferMethod(void)247 const BSL_UIO_Method *BSL_UIO_BufferMethod(void)
248 {
249 static const BSL_UIO_Method m = {
250 BSL_UIO_BUFFER,
251 BufferWrite,
252 NULL,
253 BufferCtrl,
254 NULL,
255 NULL,
256 BufferCreate,
257 BufferDestroy,
258 };
259 return &m;
260 }
261 #endif /* HITLS_BSL_UIO_BUFFER */
262