File Coverage

SecretBuffer.xs
Criterion Covered Total %
statement 476 606 78.5
branch 323 518 62.3
condition n/a
subroutine n/a
pod n/a
total 799 1124 71.0


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #define NEED_mg_findext
6             #define NEED_newSVpvn_share
7             #define NEED_SvRX
8             #define NEED_RX_PRECOMP
9             #define NEED_RX_PRELEN
10             #include "ppport.h"
11             /* these weren't supplied by ppport.h */
12             #ifndef RX_PRECOMP
13             #define RX_PRECOMP(rx) ((rx)->precomp)
14             #define RX_PRELEN(rx) ((rx)->prelen)
15             #endif
16              
17             #include "SecretBuffer_config.h"
18             #include "SecretBuffer.h"
19             #include "SecretBufferManualLinkage.h"
20              
21             /* struct attached to ::Span objects */
22             typedef struct secret_buffer_span {
23             size_t pos, lim;
24             int encoding;
25             const char *last_error;
26             } secret_buffer_span;
27              
28             /* struct used for making lists of byte ranges prior to copying things */
29             typedef struct secret_buffer_byte_range {
30             const U8 *ptr;
31             STRLEN len;
32             } secret_buffer_byte_range;
33              
34             // For typemap
35             typedef secret_buffer_span *auto_secret_buffer_span;
36              
37             int sb_parse_codepointcmp(secret_buffer_parse *lhs, secret_buffer_parse *rhs);
38              
39             /**********************************************************************************************\
40             * XS Utils
41             \**********************************************************************************************/
42              
43             /* Common perl idioms for negative offset or negative count meaning a position
44             * measured backward from the end.
45             */
46 489           static inline IV normalize_offset(IV ofs, IV len) {
47 489 100         if (ofs < 0) {
48 20           ofs += len;
49 20 50         if (ofs < 0)
50 0           ofs= 0;
51             }
52 469 100         else if (ofs > len)
53 10           ofs= len;
54 489           return ofs;
55             }
56              
57             /* For exported constant dualvars */
58             #define EXPORT_ENUM(x) newCONSTSUB(stash, #x, make_enum_dualvar(aTHX_ x, newSVpvs_share(#x)))
59 1326           static SV * make_enum_dualvar(pTHX_ IV ival, SV *name) {
60 1326 50         SvUPGRADE(name, SVt_PVNV);
61 1326           SvIV_set(name, ival);
62 1326           SvIOK_on(name);
63 1326           SvREADONLY_on(name);
64 1326           return name;
65             }
66              
67             /**********************************************************************************************\
68             * Platform compatibility stuff
69             \**********************************************************************************************/
70              
71             #ifdef WIN32
72              
73             static size_t get_page_size() {
74             SYSTEM_INFO sysInfo;
75             GetSystemInfo(&sysInfo);
76             return sysInfo.dwPageSize;
77             }
78              
79             typedef DWORD syserror_type;
80             #define GET_SYSERROR(x) ((x)= GetLastError())
81             #define SET_SYSERROR(x) SetLastError(x)
82              
83             static void croak_with_syserror(const char *prefix, DWORD error_code) {
84             char message_buffer[512];
85             DWORD length;
86              
87             length = FormatMessageA(
88             FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
89             NULL,
90             error_code,
91             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
92             message_buffer,
93             sizeof(message_buffer),
94             NULL
95             );
96              
97             if (length)
98             croak("%s: %s (%lu)", prefix, message_buffer, error_code);
99             else
100             croak("%s: %lu", prefix, error_code);
101             }
102              
103             #include "perl_win32_excerpt.c"
104              
105             #else /* not WIN32 */
106              
107 125           static size_t get_page_size() {
108 125           long pagesize = sysconf(_SC_PAGESIZE);
109 125 50         return (pagesize < 0)? 4096 : pagesize;
110             }
111              
112             #define GET_SYSERROR(x) ((x)= errno)
113             #define SET_SYSERROR(x) (errno= (x))
114             typedef int syserror_type;
115              
116             #define croak_with_syserror(msg, err) croak("%s: %s", msg, strerror(err))
117              
118             #endif
119              
120             /* Shim for systems that lack memmem */
121             #ifndef HAVE_MEMMEM
122             static void* memmem(
123             const void *haystack, size_t haystacklen,
124             const void *needle, size_t needlelen
125             ) {
126             if (!needle || !needlelen) {
127             return (void*) haystack;
128             }
129             else if (!haystack || !haystacklen) {
130             return NULL;
131             }
132             else {
133             const char *p= (const char*) haystack;
134             const char *lim= p + haystacklen - (needlelen - 1);
135             char first_ch= *(char*)needle;
136             while (p < lim) {
137             if (*p == first_ch) {
138             if (memcmp(p, needle, needlelen) == 0)
139             return (void*)p;
140             }
141             ++p;
142             }
143             }
144             return NULL;
145             }
146             #endif /* HAVE_MEMMEM */
147              
148             /* static functions sb_wait_fh_readable and sb_wait_fd_readable */
149             #include "secret_buffer_wait_readable.c"
150              
151             /**********************************************************************************************\
152             * MAGIC vtables
153             \**********************************************************************************************/
154              
155             #ifdef USE_ITHREADS
156             static int secret_buffer_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param);
157             static int secret_buffer_stringify_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *params);
158             static int secret_buffer_async_result_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *params);
159             static int secret_buffer_span_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *params);
160             #else
161             #define secret_buffer_magic_dup 0
162             #define secret_buffer_stringify_magic_dup 0
163             #define secret_buffer_async_result_magic_dup 0
164             #define secret_buffer_span_magic_dup 0
165             #endif
166              
167             static int secret_buffer_magic_free(pTHX_ SV *sv, MAGIC *mg);
168             static MGVTBL secret_buffer_magic_vtbl = {
169             NULL, NULL, NULL, NULL,
170             secret_buffer_magic_free,
171             NULL,
172             secret_buffer_magic_dup
173             #ifdef MGf_LOCAL
174             ,NULL
175             #endif
176             };
177              
178             static int secret_buffer_stringify_magic_get(pTHX_ SV *sv, MAGIC *mg);
179             static int secret_buffer_stringify_magic_set(pTHX_ SV *sv, MAGIC *mg);
180             static int secret_buffer_stringify_magic_free(pTHX_ SV *sv, MAGIC *mg);
181             static MGVTBL secret_buffer_stringify_magic_vtbl = {
182             secret_buffer_stringify_magic_get,
183             secret_buffer_stringify_magic_set,
184             NULL, NULL,
185             secret_buffer_stringify_magic_free,
186             NULL,
187             secret_buffer_stringify_magic_dup
188             #ifdef MGf_LOCAL
189             ,NULL
190             #endif
191             };
192              
193             static int secret_buffer_async_result_magic_free(pTHX_ SV *sv, MAGIC *mg);
194             static MGVTBL secret_buffer_async_result_magic_vtbl = {
195             NULL, NULL, NULL, NULL,
196             secret_buffer_async_result_magic_free,
197             NULL,
198             secret_buffer_async_result_magic_dup
199             #ifdef MGf_LOCAL
200             ,NULL
201             #endif
202             };
203              
204             static int secret_buffer_span_magic_free(pTHX_ SV *sv, MAGIC *mg);
205             static MGVTBL secret_buffer_span_magic_vtbl = {
206             NULL, NULL, NULL, NULL,
207             secret_buffer_span_magic_free,
208             NULL,
209             secret_buffer_span_magic_dup
210             #ifdef MGf_LOCAL
211             ,NULL
212             #endif
213             };
214              
215             typedef void* secret_buffer_X_auto_ctor(pTHX_ SV *owner);
216 12428           void* secret_buffer_X_from_magic(pTHX_ SV *obj, int flags,
217             const MGVTBL *mg_vtbl, const char *mg_desc,
218             secret_buffer_X_auto_ctor *auto_ctor
219             ) {
220             SV *sv;
221             MAGIC *magic;
222              
223 12428 50         if ((!obj || !SvOK(obj)) && (flags & SECRET_BUFFER_MAGIC_UNDEF_OK))
    50          
    0          
224 0           return NULL;
225              
226 12428 100         if (!sv_isobject(obj)) {
227 1514 50         if (flags & SECRET_BUFFER_MAGIC_OR_DIE)
228 0           croak("Not an object");
229 1514           return NULL;
230             }
231 10914           sv = SvRV(obj);
232 10914 100         if (SvMAGICAL(sv) && (magic = mg_findext(sv, PERL_MAGIC_ext, mg_vtbl)))
    100          
233 8772           return magic->mg_ptr;
234              
235 2142 100         if (flags & SECRET_BUFFER_MAGIC_AUTOCREATE && auto_ctor) {
    50          
236 1597           void *data= auto_ctor(aTHX_ sv);
237 1597           magic = sv_magicext(sv, NULL, PERL_MAGIC_ext, mg_vtbl, (const char*) data, 0);
238             #ifdef USE_ITHREADS
239             magic->mg_flags |= MGf_DUP;
240             #endif
241 1597           return data;
242             }
243 545 50         if (flags & SECRET_BUFFER_MAGIC_OR_DIE)
244 0           croak("Object lacks '%s' magic", mg_desc);
245 545           return NULL;
246             }
247              
248             static secret_buffer_span* secret_buffer_span_from_magic(SV *objref, int flags);
249              
250             #include "secret_buffer_base.c"
251             #include "secret_buffer_charset.c"
252             #include "secret_buffer_parse.c"
253             #include "secret_buffer_span.c"
254             #include "secret_buffer_console.c"
255             #include "secret_buffer_async_write.c"
256              
257             /**********************************************************************************************\
258             * SecretBuffer magic
259             \**********************************************************************************************/
260              
261             /*
262             * SecretBuffer Magic
263             */
264              
265 582           int secret_buffer_magic_free(pTHX_ SV *sv, MAGIC *mg) {
266 582           secret_buffer *buf= (secret_buffer*) mg->mg_ptr;
267 582 50         if (buf) {
268 582           secret_buffer_realloc(buf, 0);
269 582 100         if (buf->stringify_sv)
270 44           sv_2mortal(buf->stringify_sv);
271 582           Safefree(mg->mg_ptr);
272 582           mg->mg_ptr = NULL;
273             }
274 582           return 0;
275             }
276              
277             #ifdef USE_ITHREADS
278             int secret_buffer_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
279             secret_buffer *clone, *orig = (secret_buffer *)mg->mg_ptr;
280             PERL_UNUSED_VAR(param);
281             Newxz(clone, 1, secret_buffer);
282             clone->wrapper= mg->mg_obj;
283             mg->mg_ptr = (char *)clone;
284             secret_buffer_realloc(clone, orig->capacity);
285             if (orig->capacity)
286             memcpy(clone->data, orig->data, orig->capacity);
287             clone->len= orig->len;
288             return 0;
289             }
290             #endif
291              
292             /* Aliases for typemap */
293             typedef secret_buffer *auto_secret_buffer;
294             typedef secret_buffer *maybe_secret_buffer;
295              
296             /*
297             * SecretBuffer stringify SV magic
298             */
299              
300 87           int secret_buffer_stringify_magic_get(pTHX_ SV *sv, MAGIC *mg) {
301 87           secret_buffer *buf= (secret_buffer *)mg->mg_ptr;
302             assert(buf->stringify_sv == sv);
303 87 100         if (buf->data) {
304             /* Perl SvPV requires there to be a NUL byte beyond the reported length of the SV */
305 86 50         if (buf->capacity <= buf->len) {
306 0           secret_buffer_alloc_at_least(buf, buf->len+1);
307 0           buf->data[buf->len]= 0;
308             }
309 86           SvPVX(sv)= buf->data;
310 86           SvCUR(sv)= buf->len;
311             } else {
312 1           SvPVX(sv)= "";
313 1           SvCUR(sv)= 0;
314             }
315 87           SvPOK_on(sv);
316 87           SvUTF8_off(sv);
317 87           SvREADONLY_on(sv);
318 87           return 0;
319             }
320              
321 0           int secret_buffer_stringify_magic_set(pTHX_ SV *sv, MAGIC *mg) {
322 0           warn("Attempt to assign stringify scalar");
323 0           return 0;
324             }
325              
326 44           int secret_buffer_stringify_magic_free(pTHX_ SV *sv, MAGIC *mg) {
327             /* warn("Freeing stringify scalar"); */
328 44           return 0;
329             }
330              
331             #ifdef USE_ITHREADS
332             int secret_buffer_stringify_magic_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
333             croak("Can't dup stringify_sv");
334             }
335             #endif
336              
337 74           SV* secret_buffer_get_stringify_sv(secret_buffer *buf) {
338             dTHX;
339             MAGIC mg;
340 74           SV *sv= buf->stringify_sv;
341 74 100         if (!sv) {
342 44           sv= buf->stringify_sv= newSV(0);
343 44           sv_magicext(sv, NULL, PERL_MAGIC_ext, &secret_buffer_stringify_magic_vtbl, (const char *)buf, 0);
344             #ifdef USE_ITHREADS
345             /* magic->mg_flags |= MGf_DUP; it doesn't support duplication, so does the flag need set? */
346             #endif
347             }
348             /* Run the get magic immediately in case caller forgets */
349 74           mg.mg_ptr= (char*) buf;
350 74           secret_buffer_stringify_magic_get(aTHX_ sv, &mg);
351 74           return sv;
352             }
353              
354             /* flag for capacity */
355             #define SECRET_BUFFER_AT_LEAST 1
356              
357             /* Convenience to convert string parameters to the corresponding integer so that Perl-side
358             * doesn't always need to import the flag constants.
359             */
360 1           static IV parse_io_flags(pTHX_ SV *sv) {
361 1 50         if (!sv || !SvOK(sv))
    50          
362 0           return 0;
363 1 50         if (SvIOK(sv))
364 1           return SvIV(sv);
365 0 0         if (SvPOK(sv)) {
366 0           const char *str= SvPV_nolen(sv);
367 0 0         if (!str[0]) return 0;
368 0 0         if (strcmp(str, "NONBLOCK") == 0) return SECRET_BUFFER_NONBLOCK;
369             }
370 0           croak("Unknown flag %s", SvPV_nolen(sv));
371             }
372              
373 2           static IV parse_alloc_flags(pTHX_ SV *sv) {
374 2 50         if (!sv || !SvOK(sv))
    50          
375 0           return 0;
376 2 50         if (SvIOK(sv))
377 0           return SvIV(sv);
378 2 50         if (SvPOK(sv)) {
379 2           const char *str= SvPV_nolen(sv);
380 2 50         if (!str[0]) return 0;
381 2 50         if (strcmp(str, "AT_LEAST") == 0) return SECRET_BUFFER_AT_LEAST;
382             }
383 0           croak("Unknown flag %s", SvPV_nolen(sv));
384             }
385              
386             /* encoding-type for Span->parse_lenprefixed */
387             #define BASE128LE 1
388             #define BASE128BE 2
389             #define ASN1_DER_LENGTH 3
390              
391 0           static IV parse_lenprefixed_type(pTHX_ SV *sv) {
392 0 0         if (sv && SvIOK(sv)) {
    0          
393 0           IV val= SvIV(sv);
394 0 0         if (val < 1 || val > 3)
    0          
395 0           croak("Unknown encoding type %ld", (long)val);
396 0           return val;
397 0 0         } else if (sv && SvOK(sv)) {
    0          
398             STRLEN len;
399 0           const char *str= SvPV(sv, len);
400 0 0         if (strcmp(str, "BASE128LE") == 0) return BASE128LE;
401 0 0         if (strcmp(str, "BASE128BE") == 0) return BASE128BE;
402 0 0         if (strcmp(str, "ASN1_DER_LENGTH") == 0) return ASN1_DER_LENGTH;
403 0           croak("Unknown encoding type '%s'", str);
404             }
405 0           }
406              
407             /* for typemap to automatically convert flags */
408             typedef int secret_buffer_io_flags;
409             typedef int secret_buffer_alloc_flags;
410             typedef int secret_buffer_lenprefixed_type;
411              
412             typedef sb_console_state maybe_console_state;
413             typedef sb_console_state auto_console_state;
414              
415             /**********************************************************************************************\
416             * Debug helpers
417             \**********************************************************************************************/
418              
419             /* Helper function to check if a memory page is accessible (committed and readable) */
420             #if defined(WIN32)
421             #define CAN_SCAN_MEMORY 1
422             static bool is_page_accessible(uintptr_t addr) {
423             MEMORY_BASIC_INFORMATION memInfo;
424             if (VirtualQuery((LPCVOID)addr, &memInfo, sizeof(memInfo)) == 0)
425             return FALSE;
426             return (memInfo.State == MEM_COMMIT) &&
427             (memInfo.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE));
428             }
429             #elif defined(HAVE_MINCORE)
430             #define CAN_SCAN_MEMORY 1
431             #include
432 28881           static bool is_page_accessible(uintptr_t addr) {
433             unsigned char vec;
434 28881           return mincore((void*)addr, 1, &vec) == 0;
435             }
436             #else
437             #define CAN_SCAN_MEMORY 0
438             #endif
439              
440             /* The rest only works if we have is_page_accessible */
441             #if CAN_SCAN_MEMORY
442 125           IV scan_mapped_memory_in_range(uintptr_t p, uintptr_t lim, const char *needle, size_t needle_len) {
443 125           size_t pagesize= get_page_size();
444 125           size_t count= 0;
445             void *at;
446 125           uintptr_t run_start = p, run_lim;
447 125           p = (p & ~(pagesize - 1)); /* round to nearest page, from here out */
448 249 100         while (p < lim) {
449             /* Skip pages that aren't mapped */
450 124 50         while (p < lim && !is_page_accessible(p)) {
    50          
451 0           p += pagesize;
452 0           run_start= p;
453             }
454             /* This page is mapped. Find the end of this mapped range, if it comes before lim */
455 28881 100         while (p < lim && is_page_accessible(p)) {
    50          
456 28757           p += pagesize;
457             }
458 124           run_lim= p < lim? p : lim;
459             /* Scan memory from run_start to run_lim */
460 131 50         while (run_start < run_lim && (at= memmem((void*)run_start, run_lim - run_start, needle, needle_len))) {
    100          
461 7           ++count;
462 7           run_start= ((intptr_t)at) + needle_len;
463             }
464             }
465 125           return count;
466             }
467             #else
468             IV scan_mapped_memory_in_range(uintptr_t p, uintptr_t lim, const char *needle, size_t needle_len) {
469             return -1;
470             }
471             #endif
472              
473             /**********************************************************************************************\
474             * Crypt::SecretBuffer API
475             \**********************************************************************************************/
476             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer
477             PROTOTYPES: DISABLE
478              
479             void
480             new(...)
481             ALIAS:
482             Crypt::SecretBuffer::Exports::secret = 1
483             Crypt::SecretBuffer::Exports::secret_buffer = 2
484             INIT:
485 549           SV *buf_ref= NULL;
486 549           secret_buffer *buf= secret_buffer_new(0, &buf_ref);
487 549           int i, next_arg= ix == 0? 1 : 0;
488             PPCODE:
489 549 100         if (items - next_arg == 1) {
490 110           secret_buffer_splice_sv(buf, 0, 0, ST(next_arg));
491             }
492             else {
493 439 50         if ((items - next_arg) & 1)
494 0           croak("Odd number of arguments; expected (key => value) pairs");
495 444 100         for (i= next_arg; i < items-1; i += 2) {
496 5           SV *key= ST(i), *val= ST(i+1);
497             {
498 5           dSP;
499 5           ENTER;
500 5           SAVETMPS;
501 5 50         PUSHMARK(SP);
502 5 50         EXTEND(SP, 2);
503 5           PUSHs(buf_ref);
504 5           PUSHs(val);
505 5           PUTBACK;
506 5           call_method(SvPV_nolen(key), G_DISCARD);
507 5 50         FREETMPS;
508 5           LEAVE;
509             }
510             }
511             }
512 549           PUSHs(buf_ref);
513              
514             void
515             append(buf, ...)
516             auto_secret_buffer buf
517             ALIAS:
518             assign = 1
519             INIT:
520             int i;
521 30 100         size_t from_ofs= (ix? 0 : buf->len), len_sum= 0;
522             U8 *write_pos;
523 30           secret_buffer_byte_range stack_ranges[16], *ranges= stack_ranges;
524 30 50         if ((items-1) > 16) {
525 0           Newx(ranges, (items-1), secret_buffer_byte_range);
526 0           SAVEFREEPV(ranges);
527             }
528             PPCODE:
529 60 100         for (i= 0; i < items-1; i++) {
530 30           ranges[i].ptr= secret_buffer_SvPVbyte(ST(i+1), &ranges[i].len);
531 30           len_sum += ranges[i].len;
532             }
533 30           secret_buffer_set_len(buf, from_ofs + len_sum);
534 30           write_pos= buf->data + from_ofs;
535 60 100         for (i= 0; i < items-1; i++) {
536 30           memcpy(write_pos, ranges[i].ptr, ranges[i].len);
537 30           write_pos += ranges[i].len;
538             }
539 30           XSRETURN(1); /* return self for chaining */
540              
541             void
542             length(buf, val=NULL)
543             auto_secret_buffer buf
544             SV *val
545             ALIAS:
546             len = 1
547             PPCODE:
548 176 100         if (val) { /* writing */
549 8           IV ival= SvIV(val);
550 8 50         if (ival < 0) ival= 0;
551 8           secret_buffer_set_len(buf, ival);
552             /* return self, for chaining */
553             }
554             else /* reading */
555 168           ST(0)= sv_2mortal(newSViv(buf->len));
556 176           XSRETURN(1);
557              
558             void
559             capacity(buf, val=NULL, flags= 0)
560             auto_secret_buffer buf
561             SV *val
562             secret_buffer_alloc_flags flags
563             PPCODE:
564 8 100         if (val) { /* wiritng */
565 4           IV ival= SvIV(val);
566 4 50         if (ival < 0) ival= 0;
567 4 100         if (flags & SECRET_BUFFER_AT_LEAST)
568 2           secret_buffer_alloc_at_least(buf, ival);
569             else
570 2           secret_buffer_realloc(buf, ival);
571             /* return self, for chaining */
572             }
573             else /* reading */
574 4           ST(0)= sv_2mortal(newSViv(buf->capacity));
575 8           XSRETURN(1);
576              
577             void
578             clear(buf)
579             auto_secret_buffer buf
580             PPCODE:
581 7           secret_buffer_realloc(buf, 0);
582 7           XSRETURN(1); /* self, for chaining */
583              
584             IV
585             index(buf, pattern, ofs_sv= &PL_sv_undef)
586             secret_buffer *buf
587             SV *pattern
588             SV *ofs_sv
589             ALIAS:
590             rindex = 1
591             INIT:
592             secret_buffer_parse parse;
593 88           size_t pos= 0, lim;
594 88           int flags= 0;
595 88 100         if (ix == 0) { // index (forward)
596 79 100         pos= normalize_offset(SvOK(ofs_sv)? SvIV(ofs_sv) : 0, buf->len);
597 79           lim= buf->len;
598             } else { // rindex (reverse)
599 9 100         IV max= normalize_offset(SvOK(ofs_sv)? SvIV(ofs_sv) : -1, buf->len);
600 9           flags= SECRET_BUFFER_MATCH_REVERSE;
601             // The ofs specifies the *start* of the match, not the maximum byte pos
602             // that could be part of the match. If pattern is a charset, add one to get 'lim',
603             // and if pattern is a string, add string byte length to get 'lim'.
604 9 100         if (SvRX(pattern))
605 2           lim= max + 1;
606             else {
607             STRLEN len; // needs to be byte count, so can't SvCUR without converting to bytes first
608 7           (void) SvPVbyte(pattern, len);
609 7           lim= max + len;
610             }
611             // re-clamp lim to end of buffer
612 9 100         if (lim > buf->len) lim= buf->len;
613             }
614 88 50         if (!secret_buffer_parse_init(&parse, buf, pos, lim, 0))
615 0           croak("%s", parse.error);
616             CODE:
617 88 100         if (secret_buffer_match(&parse, pattern, flags))
618 30           RETVAL= parse.pos - (U8*) buf->data;
619             else {
620 58 50         if (parse.error)
621 0           croak("%s", parse.error);
622 58           RETVAL= -1;
623             }
624             OUTPUT:
625             RETVAL
626              
627             void
628             scan(buf, pattern, flags= 0, ofs= 0, len_sv= &PL_sv_undef)
629             secret_buffer *buf
630             SV *pattern
631             IV flags
632             IV ofs
633             SV *len_sv
634             INIT:
635             secret_buffer_parse parse;
636             // lim was captured as an SV so that undef can be used to indicate
637             // end of the buffer.
638 106 50         IV len= !SvOK(len_sv)? buf->len : SvIV(len_sv);
639 106           ofs= normalize_offset(ofs, buf->len);
640 106 50         if (!secret_buffer_parse_init(&parse, buf,
641 106           ofs, ofs + normalize_offset(len, buf->len - ofs),
642             (flags & SECRET_BUFFER_ENCODING_MASK)
643             ))
644 0           croak("%s", parse.error);
645             PPCODE:
646 106 100         if (secret_buffer_match(&parse, pattern, flags)) {
647 82           PUSHs(sv_2mortal(newSViv(parse.pos - (U8*) buf->data)));
648 82           PUSHs(sv_2mortal(newSViv(parse.lim - parse.pos)));
649             }
650 24 50         else if (parse.error)
651 0           croak("%s", parse.error);
652              
653             void
654             splice(buf, ofs, len, replacement)
655             secret_buffer *buf
656             IV ofs
657             IV len
658             SV *replacement
659             PPCODE:
660             /* normalize negative offset, and clamp to valid range */
661 0           ofs= normalize_offset(ofs, buf->len);
662             /* normalize negative count, and clamp to valid range */
663 0           len= normalize_offset(len, buf->len - ofs);
664 0           secret_buffer_splice_sv(buf, ofs, len, replacement);
665 0           XSRETURN(1); /* return $self */
666              
667             void
668             substr(buf, ofs, count_sv=NULL, replacement=NULL)
669             secret_buffer *buf
670             IV ofs
671             SV *count_sv
672             SV *replacement
673             INIT:
674             unsigned char *sub_start;
675 18           secret_buffer *sub_buf= NULL;
676 18           SV *sub_ref= NULL;
677 18 100         IV count= count_sv? SvIV(count_sv) : buf->len;
678             PPCODE:
679             /* normalize negative offset, and clamp to valid range */
680 18           ofs= normalize_offset(ofs, buf->len);
681             /* normalize negative count, and clamp to valid range */
682 18           count= normalize_offset(count, buf->len - ofs);
683 18           sub_start= (unsigned char*) buf->data + ofs;
684             /* If called in non-void context, construct new secret from this range */
685 18 100         if (GIMME_V != G_VOID) {
686             SV **el;
687 11           sub_buf= secret_buffer_new(count, &sub_ref);
688 11 100         if (count) {
689 8           Copy(sub_start, sub_buf->data, count, unsigned char);
690 8           sub_buf->len= count;
691             }
692             /* inherit the stringify_mask */
693 11           el= hv_fetchs((HV*) SvRV(ST(0)), "stringify_mask", 0);
694 11 100         if (el && *el)
    50          
695             /* we know the hv isn't tied because we just created it, so no need to check success */
696 1           hv_stores((HV*) SvRV(sub_ref), "stringify_mask", newSVsv(*el));
697             }
698             /* modifying string? */
699 18 100         if (replacement)
700 7           secret_buffer_splice_sv(buf, ofs, count, replacement);
701             /* If void context, return nothing. Else return the substr */
702 18 100         if (!sub_ref)
703 7           XSRETURN(0);
704             else {
705 11           ST(0)= sub_ref; /* already mortal */
706 11           XSRETURN(1);
707             }
708              
709             void
710             append_asn1_der_length(buf, val)
711             secret_buffer *buf
712             UV val
713             PPCODE:
714 384           secret_buffer_append_uv_asn1_der_length(buf, val);
715              
716             void
717             append_base128le(buf, val)
718             secret_buffer *buf
719             UV val
720             PPCODE:
721 384           secret_buffer_append_uv_base128le(buf, val);
722              
723             void
724             append_base128be(buf, val)
725             secret_buffer *buf
726             UV val
727             PPCODE:
728 384           secret_buffer_append_uv_base128be(buf, val);
729              
730             void
731             append_lenprefixed(buf, ...)
732             secret_buffer *buf
733             INIT:
734 1           size_t bytes_needed= 0;
735             IV i;
736             PPCODE:
737             /* Add up all the lengths and over-estimate 9 bytes for each length specifier */
738 4 100         for (i= 1; i < items; i++) {
739             STRLEN len;
740 3           secret_buffer_SvPVbyte(ST(i), &len);
741 3           bytes_needed += 9 + len;
742             }
743             /* ensure space with one reallocation */
744 1           secret_buffer_alloc_at_least(buf, buf->len + bytes_needed);
745             /* append each length and span to the buffer */
746 4 100         for (i= 1; i < items; i++) {
747             STRLEN len;
748             size_t buf_pos;
749 3           const char *data= secret_buffer_SvPVbyte(ST(i), &len);
750 3           secret_buffer_append_uv_base128be(buf, len);
751 3           buf_pos= buf->len;
752 3           secret_buffer_set_len(buf, buf_pos + len);
753 3           memcpy(buf->data + buf_pos, data, len);
754             }
755              
756             IV
757             memcmp(lhs, rhs, reverse=false)
758             SV *lhs
759             SV *rhs
760             bool reverse
761             ALIAS:
762             Crypt::SecretBuffer::Span::memcmp = 1
763             Crypt::SecretBuffer::Exports::memcmp = 2
764             INIT:
765             STRLEN lhs_len, rhs_len;
766 163           const char *lhs_buf= secret_buffer_SvPVbyte(lhs, &lhs_len);
767 163           const char *rhs_buf= secret_buffer_SvPVbyte(rhs, &rhs_len);
768             PERL_UNUSED_VAR(ix);
769             CODE:
770             /* constant-time loop */
771             {
772 163           volatile int ret= 0;
773 163           int i, common_len= lhs_len < rhs_len? lhs_len : rhs_len;
774 1415 100         for (i = 0; i < common_len; i++)
775 1252 100         if (lhs_buf[i] != rhs_buf[i] && !ret)
    50          
776 8 100         ret= lhs_buf[i] < rhs_buf[i]? -1 : 1;
777 163 100         if (ret == 0 && lhs_len != rhs_len)
    100          
778 6 100         ret= lhs_len < rhs_len? -1 : 1;
779 163           RETVAL= ret;
780             }
781 163 100         if (reverse)
782 4           RETVAL= -RETVAL;
783             OUTPUT:
784             RETVAL
785              
786             UV
787             append_random(buf, count, flags=0)
788             auto_secret_buffer buf
789             UV count
790             secret_buffer_io_flags flags
791             CODE:
792 2 50         RETVAL= secret_buffer_append_random(buf, count, flags);
793             OUTPUT:
794             RETVAL
795              
796             void
797             append_sysread(buf, handle, count)
798             auto_secret_buffer buf
799             PerlIO *handle
800             IV count
801             INIT:
802             IV got;
803             PPCODE:
804 12           got= secret_buffer_append_sysread(buf, handle, count);
805 12 50         if (got < 0)
806 0           XSRETURN_UNDEF;
807             else
808 12           PUSHs(sv_2mortal(newSViv(got)));
809              
810             void
811             append_read(buf, handle, count)
812             auto_secret_buffer buf
813             PerlIO *handle
814             IV count
815             INIT:
816             int got;
817             PPCODE:
818 60           got= secret_buffer_append_read(buf, handle, count);
819 60 100         if (got < 0)
820 1           XSRETURN_UNDEF;
821             else
822 59           PUSHs(sv_2mortal(newSViv(got)));
823              
824             void
825             _append_console_line(buf, handle)
826             auto_secret_buffer buf
827             PerlIO *handle
828             INIT:
829             int got;
830             PPCODE:
831 40           got= secret_buffer_append_console_line(buf, handle);
832 40           ST(0)= got == SECRET_BUFFER_GOTLINE? &PL_sv_yes
833 49 100         : got == SECRET_BUFFER_EOF? &PL_sv_no
834 9 100         : &PL_sv_undef;
835 40           XSRETURN(1);
836              
837             void
838             syswrite(buf, io, count=buf->len, ofs=0)
839             secret_buffer *buf
840             PerlIO *io
841             IV ofs
842             IV count
843             INIT:
844             IV wrote;
845             PPCODE:
846 4           wrote= secret_buffer_syswrite(buf, io, ofs, count);
847 4 50         ST(0)= (wrote < 0)? &PL_sv_undef : sv_2mortal(newSViv(wrote));
848 4           XSRETURN(1);
849              
850             void
851             write_async(buf, io, count=buf->len, ofs=0)
852             secret_buffer *buf
853             PerlIO *io
854             IV ofs
855             IV count
856             INIT:
857             IV wrote;
858 3           SV *ref_out= NULL;
859             PPCODE:
860 3 100         wrote= secret_buffer_write_async(buf, io, ofs, count, GIMME_V == G_VOID? NULL : &ref_out);
861             /* wrote == 0 means that it supplied a result promise object, which is already mortal.
862             * but avoid creating one when called in void context. */
863 3 50         ST(0)= wrote? sv_2mortal(newSViv(wrote)) : ref_out? ref_out : &PL_sv_undef;
    0          
864 3           XSRETURN(1);
865              
866             void
867             stringify(buf, ...)
868             auto_secret_buffer buf
869             INIT:
870 73           SV **field= hv_fetch((HV*)SvRV(ST(0)), "stringify_mask", 14, 0);
871             PPCODE:
872 73 100         if (!field || !*field) {
    50          
873 12           ST(0)= sv_2mortal(newSVpvn("[REDACTED]", 10));
874 61 100         } else if (SvOK(*field)) {
875 3           ST(0)= *field;
876             } else {
877 58           ST(0)= secret_buffer_get_stringify_sv(buf);
878             }
879 73           XSRETURN(1);
880              
881             void
882             unmask_to(buf, coderef)
883             secret_buffer *buf
884             SV *coderef
885             INIT:
886 9           int count= 0;
887             PPCODE:
888 9 50         PUSHMARK(SP);
889 9 50         EXTEND(SP, 1);
890 9           PUSHs(secret_buffer_get_stringify_sv(buf));
891 9           PUTBACK;
892 9           count= call_sv(coderef, GIMME_V);
893 7           SPAGAIN;
894 7           XSRETURN(count);
895              
896             bool
897             _can_count_copies_in_process_memory()
898             CODE:
899 0 0         RETVAL= false;
900             OUTPUT:
901             RETVAL
902            
903             IV
904             _count_matches_in_mem(buf, addr0, addr1)
905             secret_buffer *buf
906             UV addr0
907             UV addr1
908             CODE:
909 125 50         if (!buf->len)
910 0           croak("Empty buffer");
911 125           RETVAL= scan_mapped_memory_in_range(addr0, addr1, buf->data, buf->len);
912 125 50         if (RETVAL < 0)
913 0           croak("Unimplemented");
914             OUTPUT:
915             RETVAL
916              
917             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::Exports
918              
919             void
920             unmask_secrets_to(coderef, ...)
921             SV *coderef
922             INIT:
923 7           int count= 0, i;
924 7           secret_buffer *buf= NULL;
925             PPCODE:
926 7 50         PUSHMARK(SP);
927 7 50         EXTEND(SP, items);
    50          
928 18 100         for (i= 1; i < items; i++) {
929 11 50         if (SvOK(ST(i)) && SvROK(ST(i)) && (buf= secret_buffer_from_magic(ST(i), 0)))
    100          
    50          
930 7           PUSHs(secret_buffer_get_stringify_sv(buf));
931             else
932 4           PUSHs(ST(i));
933             }
934 7           PUTBACK;
935 7           count= call_sv(coderef, GIMME_V);
936 5           SPAGAIN;
937 5           XSRETURN(count);
938              
939             bool
940             _wait_fh_readable(handle, timeout_sv)
941             PerlIO *handle
942             SV *timeout_sv
943             CODE:
944 34           RETVAL= sb_wait_fh_readable(aTHX_ handle, timeout_sv);
945             OUTPUT:
946             RETVAL
947              
948             void
949             _debug_charset(cset)
950             secret_buffer_charset *cset
951             INIT:
952             HV *hv;
953             PPCODE:
954 22           PUSHs(sv_2mortal((SV*)newRV_noinc((SV*)(hv= newHV()))));
955 22           hv_stores(hv, "bitmap", newSVpvn((char*)cset->bitmap, sizeof(cset->bitmap)));
956 22           hv_stores(hv, "unicode_above_7F", newSViv(cset->unicode_above_7F));
957              
958             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::AsyncResult
959              
960             void
961             wait(result, timeout=-1)
962             secret_buffer_async_result *result
963             NV timeout
964             INIT:
965             IV os_err, bytes_written;
966             PPCODE:
967 0 0         if (secret_buffer_async_result_recv(result, (IV)(timeout*1000), &bytes_written, &os_err)) {
968 0 0         EXTEND(sp, 2);
969 0           ST(0)= sv_2mortal(newSViv(bytes_written));
970 0           ST(1)= sv_2mortal(newSViv(os_err));
971 0           XSRETURN(2);
972             } else {
973 0           XSRETURN(0);
974             }
975              
976             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::ConsoleState
977              
978             void
979             new(pkg, ...)
980             const char *pkg;
981             ALIAS:
982             maybe_new = 1
983             INIT:
984             sb_console_state cstate, *magic_cstate;
985 47           PerlIO *handle= NULL;
986 47           SV *auto_restore= NULL;
987 47           SV *set_echo= NULL;
988 47           SV *set_line_input= NULL;
989             SV *objref;
990 47           bool already_set= true;
991 47 50         if (items == 2) {
992 0           IO *io = sv_2io(ST(1));
993 0 0         handle= io? IoIFP(io) : NULL;
994 47 50         } else if (items > 2) {
995 47           int i= 1;
996 47 50         if ((items - 1) & 1)
997 0           croak("expected even-length key/value attribute list");
998 94 100         for (; i < items; i+= 2) {
999             STRLEN len;
1000 47           const char *name= SvPV(ST(i), len);
1001 47           SV *val= ST(i+1);
1002 47 50         if (len == 4 && memcmp(name, "echo", 4) == 0) {
    0          
1003 0 0         if (SvOK(val)) set_echo= val;
1004             }
1005 47 50         else if (len == 6 && memcmp(name, "handle", 6) == 0) {
    50          
1006 47           IO *io = sv_2io(val);
1007 47 50         handle= io? IoIFP(io) : NULL;
1008             }
1009 0 0         else if (len == 10 && memcmp(name, "line_input", 10) == 0) {
    0          
1010 0 0         if (SvOK(val)) set_line_input= val;
1011             }
1012 0 0         else if (len == 12 && memcmp(name, "auto_restore", 12) == 0) {
    0          
1013 0 0         if (SvOK(val)) auto_restore= val;
1014             }
1015             else {
1016 0           croak("Unknown option '%s'", name);
1017             }
1018             }
1019             }
1020 47 50         if (!handle)
1021 0           croak("'handle' is required");
1022             PPCODE:
1023 47           ST(0)= &PL_sv_undef;
1024             /* if it fails to initialize, return undef for 'maybe_new', else die */
1025 47 50         if (!sb_console_state_init(aTHX_ &cstate, handle)) {
1026 47 50         if (ix == 0)
1027 0           croak("Can't read console/tty state");
1028 47           XSRETURN(1);
1029             }
1030 0 0         if (auto_restore)
1031 0           cstate.auto_restore= SvTRUE(auto_restore);
1032 0 0         if (set_echo) {
1033 0           bool enable= SvTRUE(set_echo);
1034 0 0         if (sb_console_state_get_echo(&cstate) != enable) {
1035 0           already_set= false;
1036 0 0         if (!sb_console_state_set_echo(&cstate, enable))
1037 0           croak("set echo = %d failed", (int)enable);
1038             }
1039             }
1040 0 0         if (set_line_input) {
1041 0           bool enable= SvTRUE(set_line_input);
1042 0 0         if (sb_console_state_get_line_input(&cstate) != enable) {
1043 0           already_set= false;
1044 0 0         if (!sb_console_state_set_line_input(&cstate, enable))
1045 0           croak("set echo = %d failed", (int)enable);
1046             }
1047             }
1048             /* if user called 'maybe_new' and echo state aready matches requested
1049             state, return undef. */
1050 0 0         if (ix == 1 && (set_echo || set_line_input) && already_set)
    0          
    0          
    0          
1051 0           XSRETURN(1);
1052             /* new blessed ConsoleState object */
1053 0           ST(0)= objref= sv_2mortal(newRV_noinc(&PL_sv_yes));
1054 0           newSVrv(objref, pkg);
1055             /* move cstate into MAGIC on object */
1056 0           magic_cstate= secret_buffer_console_state_from_magic(objref, SECRET_BUFFER_MAGIC_AUTOCREATE);
1057 0           *magic_cstate= cstate;
1058             /* duplicate file handle in case user closes it */
1059 0           sb_console_state_dup_fd(magic_cstate);
1060 0           XSRETURN(1);
1061              
1062             bool
1063             echo(cstate, enable=NULL)
1064             sb_console_state *cstate
1065             SV *enable
1066             CODE:
1067 0 0         if (enable != NULL)
1068 0           sb_console_state_set_echo(cstate, SvTRUE(enable));
1069 0           RETVAL= sb_console_state_get_echo(cstate);
1070             OUTPUT:
1071             RETVAL
1072              
1073             bool
1074             line_input(cstate, enable=NULL)
1075             sb_console_state *cstate
1076             SV *enable
1077             CODE:
1078 0 0         if (enable != NULL)
1079 0           sb_console_state_set_line_input(cstate, SvTRUE(enable));
1080 0           RETVAL= sb_console_state_get_line_input(cstate);
1081             OUTPUT:
1082             RETVAL
1083              
1084             bool
1085             auto_restore(cstate, enable=NULL)
1086             sb_console_state *cstate
1087             SV *enable
1088             CODE:
1089 0 0         if (enable != NULL)
1090 0           cstate->auto_restore= SvTRUE(enable);
1091 0 0         RETVAL= cstate->auto_restore;
1092             OUTPUT:
1093             RETVAL
1094              
1095             bool
1096             restore(cstate)
1097             sb_console_state *cstate
1098             CODE:
1099 0           RETVAL= sb_console_state_restore(cstate);
1100 0 0         cstate->auto_restore= false; /* no longer run restore on destructor */
1101             OUTPUT:
1102             RETVAL
1103              
1104             bool
1105             wait_char_readable(cstate, timeout_sv=&PL_sv_undef)
1106             sb_console_state *cstate
1107             SV *timeout_sv
1108             CODE:
1109 0           RETVAL= sb_console_state_wait_char_readable(aTHX_ cstate, timeout_sv);
1110             OUTPUT:
1111             RETVAL
1112              
1113             bool
1114             _append_console_char(cstate, buf)
1115             sb_console_state *cstate
1116             secret_buffer *buf;
1117             CODE:
1118             #ifdef WIN32
1119             RETVAL= sb_append_console_char(aTHX_ cstate, buf);
1120             #else
1121 0 0         RETVAL= false;
1122             #endif
1123             OUTPUT:
1124             RETVAL
1125              
1126             MODULE = Crypt::SecretBuffer PACKAGE = Crypt::SecretBuffer::Span
1127              
1128             void
1129             new(class_or_obj, ...)
1130             SV *class_or_obj
1131             ALIAS:
1132             clone = 1
1133             subspan = 2
1134             Crypt::SecretBuffer::span = 3
1135             Crypt::SecretBuffer::Exports::span = 4
1136             INIT:
1137 584           secret_buffer_span *span= secret_buffer_span_from_magic(class_or_obj, SECRET_BUFFER_MAGIC_UNDEF_OK);
1138 56 50         SV **buf_field= span && SvTYPE(SvRV(class_or_obj)) == SVt_PVHV
1139 56           ? hv_fetchs((HV*)SvRV(class_or_obj), "buf", 0)
1140 584 100         : NULL;
1141 584 100         secret_buffer *buf= secret_buffer_from_magic(
1142             buf_field? *buf_field : class_or_obj, SECRET_BUFFER_MAGIC_UNDEF_OK
1143             );
1144 584 100         bool subspan= span && ix >= 2;
    100          
1145 584 100         IV base_pos= subspan? span->pos : 0;
1146 584           IV pos=0, lim=0, len=0, base_lim=0;
1147 584 100         int encoding= span? span->encoding : 0, i;
1148 584           SV *encoding_sv= NULL;
1149 584           bool have_pos= false, have_lim= false, have_len= false;
1150             PPCODE:
1151             //warn("items=%d span=%p buf=%p base_pos=%d", (int)items, span, buf, (int)base_pos);
1152             // 3-argument form, only usable when first arg is a buffer or refs a buffer
1153 584 100         if (buf && items >= 2 && looks_like_number(ST(1))) {
    100          
    100          
1154 29           pos= SvIV(ST(1));
1155 29           have_pos= true;
1156 29 100         if (items >= 3 && SvOK(ST(2))) {
    50          
1157 26           len= SvIV(ST(2));
1158 26           have_len= true;
1159 26 100         if (items >= 4) {
1160 2           encoding_sv= ST(3);
1161 2 50         if (items > 4)
1162 0           warn("unexpected 4th argument after encoding");
1163             }
1164             }
1165             } else { // key => value form
1166 555 50         if ((items - 1) & 1)
1167 0           croak("Odd number of arguments; expected (key => value, ...)");
1168 697 100         for (i= 1; i < items-1; i += 2) {
1169 142 100         if (0 == strcmp("pos", SvPV_nolen(ST(i)))) {
1170 50           pos= SvIV(ST(i+1));
1171 50           have_pos= true;
1172             }
1173 92 100         else if (0 == strcmp("lim", SvPV_nolen(ST(i)))) {
1174 30           lim= SvIV(ST(i+1));
1175 30           have_lim= true;
1176             }
1177 62 100         else if (0 == strcmp("len", SvPV_nolen(ST(i)))) {
1178 4           len= SvIV(ST(i+1));
1179 4           have_len= true;
1180             }
1181 58 100         else if (0 == strcmp("encoding", SvPV_nolen(ST(i)))) {
1182 56           encoding_sv= ST(i+1);
1183             }
1184 2 50         else if (0 == strcmp("buf", SvPV_nolen(ST(i)))) {
1185 2           buf= secret_buffer_from_magic(ST(i+1), SECRET_BUFFER_MAGIC_OR_DIE);
1186             }
1187             }
1188             }
1189 584 100         if (have_len && have_lim && (lim != pos + len))
    50          
    0          
1190 0           croak("Can't specify both 'len' and 'lim', make up your mind!");
1191             // buffer is required
1192 584 100         if (!buf) {
1193             /* The 'span()' exported function can accept plain scalars and upgrade them to a span object */
1194 2 50         if (ix != 4)
1195 0           croak("Require 'buf' attribute");
1196 2           buf= secret_buffer_new(0, NULL);
1197 2           secret_buffer_splice_sv(buf, 0, 0, class_or_obj);
1198             }
1199 584 100         base_lim= subspan? span->lim : buf->len;
1200             // pos is relative to base_pos, and needs truncated to (or count backward from) base_lim
1201 79           pos= have_pos? normalize_offset(pos, base_lim-base_pos)+base_pos
1202 663 100         : span ? span->pos
    100          
1203             : base_pos;
1204             // likewise for lim, but also might need calculated from 'len'
1205 30           lim= have_lim? normalize_offset(lim, base_lim-base_pos)+base_pos
1206 646 100         : have_len? normalize_offset(len, base_lim-pos)+pos
    100          
    100          
1207 32           : span ? span->lim
1208             : base_lim;
1209 584 50         if (pos > lim)
1210 0           croak("lim must be greater or equal to pos");
1211             //warn(" base_lim=%d pos=%d lim=%d", (int) base_lim, (int)pos, (int)lim);
1212             // check encoding
1213 584 100         if (encoding_sv) {
1214 58 50         if (!parse_encoding(aTHX_ encoding_sv, &encoding))
1215 0           croak("Unknown encoding '%s'", SvPV_nolen(encoding_sv));
1216             }
1217 584           PUSHs(new_mortal_span_obj(aTHX_ buf, pos, lim, encoding));
1218              
1219             UV
1220             pos(span, newval_sv= NULL)
1221             secret_buffer_span *span
1222             SV *newval_sv
1223             ALIAS:
1224             lim = 1
1225             len = 2
1226             length = 2
1227             CODE:
1228 664 100         if (newval_sv) {
1229 74           IV newval= SvIV(newval_sv);
1230 74 50         if (newval < 0)
1231 0           croak("pos, lim, and len cannot be negative");
1232 74           switch (ix) {
1233 64           case 0: span->pos= newval; break;
1234 10 50         case 1: if (newval < span->pos) croak("lim must be >= pos");
1235 10           span->lim= newval; break;
1236 0           case 2: span->lim= span->pos + newval;
1237 0           default: croak("BUG");
1238             }
1239             }
1240 664 100         RETVAL= ix == 0? span->pos
1241 1147 100         : ix == 1? span->lim
1242 890 100         : ix == 2? span->lim - span->pos
1243 407 50         : -1;
1244             OUTPUT:
1245             RETVAL
1246              
1247             void
1248             encoding(span, newval_sv= NULL)
1249             secret_buffer_span *span
1250             SV *newval_sv
1251             INIT:
1252             SV *enc_const;
1253 32           AV *encodings= get_av("Crypt::SecretBuffer::_encodings", 0);
1254 32 50         if (!encodings) croak("BUG");
1255             PPCODE:
1256 32 100         if (newval_sv)
1257 21 50         if (!parse_encoding(aTHX_ newval_sv, &span->encoding))
1258 0           croak("Invalid encoding");
1259 32           enc_const= *av_fetch(encodings, span->encoding, 1);
1260 32 50         if (!enc_const || !SvOK(enc_const))
    50          
1261 0           croak("BUG");
1262 32           PUSHs(enc_const);
1263              
1264             const char *
1265             last_error(span)
1266             secret_buffer_span *span
1267             CODE:
1268 5           RETVAL= span->last_error;
1269             OUTPUT:
1270             RETVAL
1271              
1272             void
1273             set_up_us_the_bom(self)
1274             SV *self
1275             ALIAS:
1276             consume_bom = 1
1277             INIT:
1278 3           secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
1279             secret_buffer_parse p;
1280 3 50         if (!secret_buffer_parse_init_from_sv(&p, self))
1281 0           croak("%s", p.error);
1282             PERL_UNUSED_VAR(ix);
1283             PPCODE:
1284 3 50         if (p.lim - p.pos >= 3 && p.pos[0] == 0xEF && p.pos[1] == 0xBB && p.pos[2] == 0xBF) {
    100          
    50          
    50          
1285 1           span->encoding= SECRET_BUFFER_ENCODING_UTF8;
1286 1           span->pos += 3;
1287             }
1288 2 50         else if (p.lim - p.pos >= 2 && p.pos[0] == 0xFF && p.pos[1] == 0xFE) {
    100          
    50          
1289 1           span->encoding= SECRET_BUFFER_ENCODING_UTF16LE;
1290 1           span->pos += 2;
1291             }
1292 1 50         else if (p.lim - p.pos >= 2 && p.pos[0] == 0xFE && p.pos[1] == 0xFF) {
    50          
    50          
1293 1           span->encoding= SECRET_BUFFER_ENCODING_UTF16BE;
1294 1           span->pos += 2;
1295             }
1296 3           XSRETURN(1);
1297              
1298             void
1299             scan(self, pattern=NULL, flags= 0)
1300             SV *self
1301             SV *pattern
1302             IV flags
1303             ALIAS:
1304             parse = 0x102
1305             rparse = 0x203
1306             trim = 0x322
1307             ltrim = 0x422
1308             rtrim = 0x523
1309             starts_with = 0x612
1310             ends_with = 0x713
1311             INIT:
1312 776           secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
1313 776           SV **sb_sv= hv_fetchs((HV*)SvRV(self), "buf", 1);
1314 776           secret_buffer *buf= secret_buffer_from_magic(*sb_sv, SECRET_BUFFER_MAGIC_OR_DIE);
1315             secret_buffer_parse parse;
1316 776 50         if (!secret_buffer_parse_init(&parse, buf, span->pos, span->lim, span->encoding))
1317 0           croak("%s", parse.error);
1318             // Bit 0 indicates an op from the end of the buffer
1319 776 100         if (ix & 1)
1320 68           flags |= SECRET_BUFFER_MATCH_REVERSE;
1321             // Bit 1 indicates an anchored op
1322 776 100         if (ix & 2)
1323 730           flags |= SECRET_BUFFER_MATCH_ANCHORED;
1324             // Bits 4..7 indicate return type,
1325             // 0 == return a span
1326             // 1 == return bool
1327             // 2 == return self
1328 776           int ret_type= (ix >> 4) & 0xF;
1329 776 100         if (ret_type != 1 && parse.encoding == SECRET_BUFFER_ENCODING_BASE64)
    50          
1330 0           croak("Cannot perform parse, trim, or scan on base64 (pos / lim of result would not be whole bytes)");
1331 776           int op= (ix >> 8);
1332             bool matched;
1333 776 100         if (!pattern) {
1334 14 100         if (op == 3 || op == 4 || op == 5)
    100          
    50          
1335 14           pattern= get_sv("Crypt::SecretBuffer::Span::default_trim_regex", 0);
1336 14 50         if (!pattern)
1337 0           croak("pattern is required");
1338             }
1339             PPCODE:
1340 776           matched= secret_buffer_match(&parse, pattern, flags);
1341 776 100         if (parse.error)
1342 7           croak("%s", parse.error);
1343 769           switch (op) {
1344 452           case 1: // parse
1345 452 100         if (matched) span->pos= parse.lim - (U8*) buf->data;
1346 452           break;
1347 1           case 2: // rparse
1348 1 50         if (matched) span->lim= parse.pos - (U8*) buf->data;
1349 1           break;
1350 118           case 3: // trim
1351             case 4: // ltrim
1352 118 100         if (matched) span->pos= parse.lim - (U8*) buf->data;
1353 118 100         if (op == 4) break;
1354             // reset the modified parse_state and run in reverse
1355 78           parse.pos= (U8*) (buf->data + span->pos);
1356 78           parse.lim= (U8*) (buf->data + span->lim);
1357 78           flags |= SECRET_BUFFER_MATCH_REVERSE;
1358 78           matched= secret_buffer_match(&parse, pattern, flags);
1359 119           case 5: // rtrim, and trim
1360 119 100         if (matched) span->lim= parse.pos - (U8*) buf->data;
1361 119           break;
1362 769           default:
1363             (void)0; // suppress warning that not all values were handled
1364             }
1365 769 100         if (ret_type == 0) {
1366 499 100         if (!matched)
1367 77           XSRETURN_UNDEF;
1368 422 50         if (parse.pos > parse.lim || parse.lim > (U8*) buf->data + buf->len)
    50          
1369 0           croak("BUG: parse pos=%p lim=%p buf.data=%p buf.len=%ld",
1370             parse.pos, parse.lim, buf->data, (long)buf->len);
1371 422           PUSHs(new_mortal_span_obj(aTHX_ buf, parse.pos - (U8*) buf->data, parse.lim - (U8*) buf->data, span->encoding));
1372 270 100         } else if (ret_type == 1) {
1373 111 100         if (matched)
1374 69           XSRETURN_YES;
1375             else
1376 42           XSRETURN_NO;
1377             }
1378             else {
1379 159           XSRETURN(1); // use self pointer in ST(0)
1380             }
1381              
1382             void
1383             parse_lenprefixed(self, count = 1)
1384             SV *self
1385             IV count
1386             INIT:
1387 5           secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
1388             secret_buffer_parse p;
1389             UV len;
1390             size_t ofs;
1391             /* treat an invalid span as a bug, rather than returning it to the user in the err_out param */
1392 5 50         if (!secret_buffer_parse_init_from_sv(&p, self))
1393 0           croak("%s", p.error);
1394             PPCODE:
1395 14 100         while (count && p.pos < p.lim) {
    100          
1396 11 50         if (!secret_buffer_parse_uv_base128be(&p, &len)) {
1397 0           span->last_error= p.error;
1398 0           XSRETURN_EMPTY;
1399             }
1400 11 100         if (len > p.lim - p.pos) {
1401 2           span->last_error= "Length exceeds end of Span";
1402 2           XSRETURN_EMPTY;
1403             }
1404 9           ofs= p.pos - (U8*) p.sbuf->data;
1405 9 50         XPUSHs(secret_buffer_span_new_obj(p.sbuf, ofs, ofs + len, 0));
1406 9           p.pos += len;
1407 9 100         if (count > 0) --count;
1408             }
1409 3           span->pos= p.pos - (U8*) p.sbuf->data;
1410 3           span->last_error= NULL;
1411              
1412             SV*
1413             parse_asn1_der_length(self)
1414             SV *self
1415             ALIAS:
1416             parse_base128le = 1
1417             parse_base128be = 2
1418             INIT:
1419 1152           secret_buffer_span *span= secret_buffer_span_from_magic(self, SECRET_BUFFER_MAGIC_OR_DIE);
1420             secret_buffer_parse p;
1421             UV val_out;
1422             bool success;
1423             /* treat an invalid span as a bug, rather than returning it to the user as last_error */
1424 1152 50         if (!secret_buffer_parse_init_from_sv(&p, self))
1425 0           croak("%s", p.error);
1426             CODE:
1427 1152           switch (ix) {
1428 384           case 0 : success= secret_buffer_parse_uv_asn1_der_length(&p, &val_out); break;
1429 384           case 1 : success= secret_buffer_parse_uv_base128le(&p, &val_out); break;
1430 384           case 2 : success= secret_buffer_parse_uv_base128be(&p, &val_out); break;
1431 0           default: croak("BUG");
1432             }
1433 1152 50         if (success) {
1434             /* advance the span position */
1435 1152           span->pos= p.pos - (U8*) p.sbuf->data;
1436 1152           span->last_error= NULL;
1437 1152           RETVAL= newSVuv(val_out);
1438             } else {
1439 0           span->last_error= p.error;
1440 0           RETVAL= &PL_sv_undef;
1441             }
1442             OUTPUT:
1443             RETVAL
1444              
1445             void
1446             copy(self, ...)
1447             SV *self
1448             ALIAS:
1449             copy_to = 1
1450             append_to = 2
1451             INIT:
1452 102           SV *dst_sv= NULL;
1453 102           int next_arg, dst_encoding= -1;
1454             secret_buffer_parse src;
1455 102 100         if (!secret_buffer_parse_init_from_sv(&src, self))
1456 1           croak("%s", src.error);
1457             PPCODE:
1458 101 100         if (ix > 0) { /* called as 'copy_to' or 'append_to' */
1459 89 50         if (items < 2)
1460 0           croak("Missing copy/append destination");
1461 89           dst_sv= ST(1);
1462 89           next_arg= 2;
1463             }
1464             else { /* called as 'copy' */
1465 12           secret_buffer_new(0, &dst_sv);
1466 12           next_arg= 1;
1467             }
1468            
1469             // parse options
1470 101 50         if ((items - next_arg) & 1)
1471 0           croak("expected even-length list of (key => val)");
1472 124 100         for (; next_arg < items; next_arg+= 2) {
1473 23 50         if (0 == strcmp(SvPV_nolen(ST(next_arg)), "encoding")) {
1474 23 50         if (!parse_encoding(aTHX_ ST(next_arg+1), &dst_encoding))
1475 0           croak("Unknown encoding");
1476             }
1477             }
1478 101 50         if (!secret_buffer_copy_to(&src, dst_sv, dst_encoding, ix == 2))
1479 0           croak("copy failed: %s", src.error);
1480             // copy returns the SecretBuffer, but copy_to returns empty list.
1481 101 100         if (ix == 0)
1482 12           PUSHs(dst_sv);
1483              
1484             IV
1485             cmp(lhs, rhs, reverse=false)
1486             SV *lhs
1487             SV *rhs
1488             bool reverse
1489             INIT:
1490             secret_buffer_parse lhs_parse, rhs_parse;
1491 20 50         if (!secret_buffer_parse_init_from_sv(&lhs_parse, lhs))
1492 0           croak("%s", lhs_parse.error);
1493 20 50         if (!secret_buffer_parse_init_from_sv(&rhs_parse, rhs))
1494 0           croak("%s", rhs_parse.error);
1495             CODE:
1496 20           RETVAL= sb_parse_codepointcmp(&lhs_parse, &rhs_parse);
1497 20 50         if (reverse)
1498 0           RETVAL= -RETVAL;
1499             OUTPUT:
1500             RETVAL
1501              
1502             BOOT:
1503             int i;
1504 26           HV *stash= gv_stashpvs("Crypt::SecretBuffer", 1);
1505 26           HV *exports_stash= gv_stashpvs("Crypt::SecretBuffer::Exports", 1);
1506             #define EXPORT_CONST(name, const) \
1507             newCONSTSUB(stash, name, make_enum_dualvar(aTHX_ const, newSVpvs_share(name)));\
1508             hv_stores(exports_stash, name, SvREFCNT_inc(*hv_fetchs(stash, name, 0)))
1509 26           EXPORT_CONST("NONBLOCK", SECRET_BUFFER_NONBLOCK);
1510 26           EXPORT_CONST("AT_LEAST", SECRET_BUFFER_AT_LEAST);
1511 26           EXPORT_CONST("MATCH_MULTI", SECRET_BUFFER_MATCH_MULTI);
1512 26           EXPORT_CONST("MATCH_REVERSE", SECRET_BUFFER_MATCH_REVERSE);
1513 26           EXPORT_CONST("MATCH_NEGATE", SECRET_BUFFER_MATCH_NEGATE);
1514 26           EXPORT_CONST("MATCH_ANCHORED",SECRET_BUFFER_MATCH_ANCHORED);
1515 26           EXPORT_CONST("MATCH_CONST_TIME",SECRET_BUFFER_MATCH_CONST_TIME);
1516             #undef EXPORT_CONST
1517             SV *enc[SECRET_BUFFER_ENCODING_MAX+1];
1518 26           memset(enc, 0, sizeof(enc));
1519             #define EXPORT_ENCODING(name, str, const) \
1520             newCONSTSUB(stash, name, (enc[const]= make_enum_dualvar(aTHX_ const, newSVpvs_share(str)))); \
1521             hv_stores(exports_stash, name, SvREFCNT_inc(*hv_fetchs(stash, name, 0)))
1522 26           EXPORT_ENCODING("ASCII", "ASCII", SECRET_BUFFER_ENCODING_ASCII);
1523 26           EXPORT_ENCODING("ISO8859_1","ISO-8859-1", SECRET_BUFFER_ENCODING_ISO8859_1);
1524 26           EXPORT_ENCODING("UTF8", "UTF-8", SECRET_BUFFER_ENCODING_UTF8);
1525 26           EXPORT_ENCODING("UTF16LE", "UTF-16LE", SECRET_BUFFER_ENCODING_UTF16LE);
1526 26           EXPORT_ENCODING("UTF16BE", "UTF-16BE", SECRET_BUFFER_ENCODING_UTF16BE);
1527 26           EXPORT_ENCODING("HEX", "HEX", SECRET_BUFFER_ENCODING_HEX);
1528 26           EXPORT_ENCODING("BASE64", "BASE64", SECRET_BUFFER_ENCODING_BASE64);
1529             #undef EXPORT_ENCODING
1530             // Set up an array of _encodings so that the accessor can return an existing SV
1531 26           AV *encodings= get_av("Crypt::SecretBuffer::_encodings", GV_ADD);
1532 26           av_fill(encodings, SECRET_BUFFER_ENCODING_MAX);
1533 234 100         for (i= 0; i <= SECRET_BUFFER_ENCODING_MAX; i++)
1534 208 100         if (enc[i] && av_store(encodings, i, enc[i]))
    50          
1535 182           SvREFCNT_inc(enc[i]);
1536 26           SECRET_BUFFER_EXPORT_FUNCTION_POINTERS