1 /*
2 * S3 Put Object via Secure Streams minimal siv4 example
3 *
4 * Written in 2010-2020 by Andy Green <andy@warmcat.com>
5 * Amit Pachore <apachor@amazon.com>
6 * securestreams-dev@amazon.com
7 *
8 * This file is made available under the Creative Commons CC0 1.0
9 * Universal Public Domain Dedication.
10 */
11
12 #include <libwebsockets.h>
13 #include <assert.h>
14 #include "ss-s3-put.h"
15
16 extern int interrupted, bad;
17
18 static lws_ss_state_return_t
ss_s3_rx(void * userobj,const uint8_t * buf,size_t len,int flags)19 ss_s3_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
20 {
21 // ss_s3_put_t *m = (ss_s3_put_t *)userobj;
22
23 if (flags & LWSSS_FLAG_EOM) {
24 bad = 0;
25 interrupted = 1; /* this example wants to exit after rx */
26 return LWSSSSRET_DESTROY_ME;
27 }
28
29 lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags);
30 lwsl_hexdump_err(buf, len);
31
32 return LWSSSSRET_OK;
33 }
34
35 static lws_ss_state_return_t
ss_s3_tx(void * userobj,lws_ss_tx_ordinal_t ord,uint8_t * buf,size_t * len,int * flags)36 ss_s3_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
37 int *flags)
38 {
39 ss_s3_put_t *m = (ss_s3_put_t *)userobj;
40
41 if (!m->pos)
42 *flags |= LWSSS_FLAG_SOM;
43
44 lwsl_user("%s: Send... total: %ld, pos: %ld\n", __func__,
45 (long)m->total, (long)m->pos);
46
47 if (*len > m->total - m->pos)
48 *len = m->total - m->pos;
49
50 if (!*len)
51 return LWSSSSRET_TX_DONT_SEND;
52
53 memcpy(buf, m->buf + m->pos, *len);
54 m->pos += *len;
55
56 if (m->pos == m->total) {
57 *flags |= LWSSS_FLAG_EOM;
58 // m->pos = 0; /* we only want to send once */
59 } else
60 return lws_ss_request_tx(m->ss);
61
62 return LWSSSSRET_OK;
63 }
64
65 static const char *awsService = "s3",
66 *awsRegion = "us-west-2",
67 *s3bucketName = "sstest2020",
68 #if 1
69 *s3ObjName = "SSs3upload2.txt";
70 #else
71 /* test huge string sigv4 hashing works */
72 *s3ObjName = "SSs3uploadaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa2.txt";
73 #endif
74 static char timestamp[32], payload_hash[65];
75 static uint8_t jpl[1 * 1024];
76
77
78 static void
create_payload(uint8_t * buf,size_t s)79 create_payload(uint8_t *buf, size_t s)
80 {
81 int i;
82
83 for (i = 0; i < (int)s; i++)
84 buf[i] = (uint8_t)('a' + i % 16);
85 }
86
set_time(char * t)87 static void set_time(char *t)
88 {
89 /*20150830T123600Z*/
90 time_t ti = time(NULL);
91 #if defined(LWS_HAVE_GMTIME_R)
92 struct tm tmp;
93 struct tm *tm = gmtime_r(&ti, &tmp);
94 #else
95 struct tm *tm = gmtime(&ti);
96 #endif
97 assert(tm);
98 strftime(t, 20, "%Y%m%dT%H%M%SZ", tm);
99 }
100
bin2hex(uint8_t * in,size_t len,char * out)101 static void bin2hex(uint8_t *in, size_t len, char *out)
102 {
103 static const char *hex = "0123456789abcdef";
104 size_t n;
105
106 for (n = 0; n < len; n++) {
107 *out++ = hex[(in[n] >> 4) & 0xf];
108 *out++ = hex[in[n] & 15];
109 }
110 *out = '\0';
111 }
112
sigv4_sha256hash_payload(uint8_t * payload,size_t len,char * hash)113 static void sigv4_sha256hash_payload(uint8_t *payload, size_t len, char *hash)
114 {
115 struct lws_genhash_ctx hash_ctx;
116 uint8_t hash_bin[32];
117
118 if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256) ||
119 /*
120 * If there is no payload, you must provide the hash of an
121 * empty string...
122 */
123 lws_genhash_update(&hash_ctx,
124 payload ? (void *)payload : (void *)"",
125 payload ? len : 0u) ||
126 lws_genhash_destroy(&hash_ctx, hash_bin))
127 {
128
129 lws_genhash_destroy(&hash_ctx, NULL);
130 lwsl_err("%s lws_genhash failed\n", __func__);
131
132 return;
133 }
134
135 bin2hex(hash_bin, 32, hash);
136 }
137
138 static lws_ss_state_return_t
ss_s3_state(void * userobj,void * sh,lws_ss_constate_t state,lws_ss_tx_ordinal_t ack)139 ss_s3_state(void *userobj, void *sh, lws_ss_constate_t state,
140 lws_ss_tx_ordinal_t ack)
141 {
142 ss_s3_put_t *m = (ss_s3_put_t *)userobj;
143
144 lwsl_user("%s: %s %s, ord 0x%x\n", __func__, lws_ss_tag(m->ss),
145 lws_ss_state_name((int)state), (unsigned int)ack);
146
147 switch (state) {
148 case LWSSSCS_CREATING:
149 create_payload(jpl, sizeof(jpl));
150 m->buf = (uint8_t *)jpl;
151 m->total = sizeof(jpl);
152
153 sigv4_sha256hash_payload(m->buf, m->total, payload_hash);
154 memset(timestamp, 0, sizeof(timestamp));
155 set_time(timestamp);
156
157 if (lws_ss_set_metadata(m->ss, "s3bucket",
158 s3bucketName, strlen(s3bucketName)) ||
159 lws_ss_set_metadata(m->ss, "s3Obj",
160 s3ObjName, strlen(s3ObjName)) ||
161 lws_ss_set_metadata(m->ss, "ctype",
162 "text/plain", strlen("text/plain")) ||
163 lws_ss_set_metadata(m->ss, "region",
164 awsRegion, strlen(awsRegion)) ||
165 lws_ss_set_metadata(m->ss, "service",
166 awsService, strlen(awsService)) ||
167 lws_ss_set_metadata(m->ss, "xacl",
168 "bucket-owner-full-control",
169 strlen("bucket-owner-full-control")) ||
170 lws_ss_set_metadata(m->ss, "xcsha256",
171 payload_hash, strlen(payload_hash)) ||
172 lws_ss_set_metadata(m->ss, "xdate",
173 timestamp, strlen(timestamp)))
174 return LWSSSSRET_DESTROY_ME;
175
176 return lws_ss_request_tx_len(m->ss, m->total);
177
178 case LWSSSCS_CONNECTED:
179 return lws_ss_request_tx(m->ss);
180
181 case LWSSSCS_DISCONNECTED:
182 return LWSSSSRET_DESTROY_ME;
183
184 case LWSSSCS_ALL_RETRIES_FAILED:
185 /* if we're out of retries, we want to close the app and FAIL */
186 bad = 1;
187 return LWSSSSRET_DESTROY_ME;
188
189 case LWSSSCS_QOS_ACK_REMOTE:
190 bad = 0;
191 break;
192
193 case LWSSSCS_QOS_NACK_REMOTE:
194 bad = 1;
195 break;
196
197 case LWSSSCS_DESTROYING:
198 interrupted = 1;
199 break;
200
201 default:
202 break;
203 }
204
205 return 0;
206 }
207
208 const lws_ss_info_t s3_ssi = {
209 .handle_offset = offsetof(ss_s3_put_t, ss),
210 .opaque_user_data_offset = offsetof(ss_s3_put_t, opaque_data),
211 .rx = ss_s3_rx,
212 .tx = ss_s3_tx,
213 .state = ss_s3_state,
214 .user_alloc = sizeof(ss_s3_put_t),
215 .streamtype = "s3PutObj"
216 };
217