1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
|
;;; GNUTLS --- Guile bindings for GnuTLS.
;;; Copyright (C) 2007 Free Software Foundation
;;;
;;; GNUTLS is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU Lesser General Public
;;; License as published by the Free Software Foundation; either
;;; version 2.1 of the License, or (at your option) any later version.
;;;
;;; GNUTLS is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;; Lesser General Public License for more details.
;;;
;;; You should have received a copy of the GNU Lesser General Public
;;; License along with GNUTLS; if not, write to the Free Software
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
;;; Written by Ludovic Courtès <ludo@chbouib.org>
(define-module (gnutls build enums)
:use-module (srfi srfi-1)
:use-module (srfi srfi-9)
:use-module (gnutls build utils)
:export (make-enum-type enum-type-subsystem enum-type-value-alist
enum-type-c-type enum-type-get-name-function
enum-type-automatic-get-name-function
enum-type-smob-name
enum-type-to-c-function enum-type-from-c-function
output-enum-smob-definitions output-enum-definitions
output-enum-declarations
output-enum-definition-function output-c->enum-converter
output-enum->c-converter
%cipher-enum %mac-enum %compression-method-enum %kx-enum
%protocol-enum %certificate-type-enum
%gnutls-enums %gnutls-extra-enums))
;;;
;;; This module helps with the creation of bindings for the C enumerate
;;; types. It aims at providing strong typing (i.e., one cannot use an
;;; enumerate value of the wrong type) along with authenticity checks (i.e.,
;;; values of a given enumerate type cannot be forged---for instance, one
;;; cannot use some random integer as an enumerate value). Additionally,
;;; Scheme enums representing the same C enum value should be `eq?'.
;;;
;;; To that end, Scheme->C conversions are optimized (a simple
;;; `SCM_SMOB_DATA'), since that is the most common usage pattern.
;;; Conversely, C->Scheme conversions take time proportional to the number of
;;; value in the enum type.
;;;
;;;
;;; Enumeration tools.
;;;
(define-record-type <enum-type>
(%make-enum-type subsystem c-type enum-map get-name value-prefix)
enum-type?
(subsystem enum-type-subsystem)
(enum-map enum-type-value-alist)
(c-type enum-type-c-type)
(get-name enum-type-get-name-function)
(value-prefix enum-type-value-prefix))
(define (make-enum-type subsystem c-type values get-name . value-prefix)
;; Return a new enumeration type.
(let ((value-prefix (if (null? value-prefix)
#f
(car value-prefix))))
(%make-enum-type subsystem c-type
(make-enum-map subsystem values value-prefix)
get-name value-prefix)))
(define (make-enum-map subsystem values value-prefix)
;; Return an alist mapping C enum values (strings) to Scheme symbols.
(define (value-symbol->string value)
(string-upcase (scheme-symbol->c-name value)))
(define (make-c-name value)
(case value-prefix
((#f)
;; automatically derive the C value name.
(string-append "GNUTLS_" (string-upcase (symbol->string subsystem))
"_" (value-symbol->string value)))
(else
(string-append value-prefix (value-symbol->string value)))))
(map (lambda (value)
(cons (make-c-name value) value))
values))
(define (enum-type-smob-name enum)
;; Return the C name of the smob type for ENUM.
(string-append "scm_tc16_gnutls_"
(scheme-symbol->c-name (enum-type-subsystem enum))
"_enum"))
(define (enum-type-smob-list enum)
;; Return the name of the C variable holding a list of value (SMOBs) for
;; ENUM. This list is used when converting from C to Scheme.
(string-append "scm_gnutls_"
(scheme-symbol->c-name (enum-type-subsystem enum))
"_enum_values"))
(define (enum-type-to-c-function enum)
;; Return the name of the C `scm_to_' function for ENUM.
(string-append "scm_to_gnutls_"
(scheme-symbol->c-name (enum-type-subsystem enum))))
(define (enum-type-from-c-function enum)
;; Return the name of the C `scm_from_' function for ENUM.
(string-append "scm_from_gnutls_"
(scheme-symbol->c-name (enum-type-subsystem enum))))
(define (enum-type-automatic-get-name-function enum)
;; Return the name of an automatically-generated C function that returns a
;; string describing the given enum value of type ENUM.
(string-append "scm_gnutls_"
(scheme-symbol->c-name (enum-type-subsystem enum))
"_to_c_string"))
;;;
;;; C code generation.
;;;
(define (output-enum-smob-definitions enum port)
(let ((smob (enum-type-smob-name enum))
(get-name (enum-type-get-name-function enum)))
(format port "SCM_GLOBAL_SMOB (~a, \"~a\", 0);~%"
smob (enum-type-subsystem enum))
(format port "SCM ~a = SCM_EOL;~%"
(enum-type-smob-list enum))
(if (not (string? get-name))
;; Generate a "get name" function.
(output-enum-get-name-function enum port))
;; Generate the printer and `->string' function.
(let ((get-name (or get-name
(enum-type-automatic-get-name-function enum))))
(let ((subsystem (scheme-symbol->c-name (enum-type-subsystem enum))))
;; SMOB printer.
(format port "SCM_SMOB_PRINT (~a, ~a_print, obj, port, pstate)~%{~%"
smob subsystem)
(format port " scm_puts (\"#<gnutls-~a-enum \", port);~%"
(enum-type-subsystem enum))
(format port " scm_puts (~a (~a (obj, 1, \"~a_print\")), port);~%"
get-name (enum-type-to-c-function enum) subsystem)
(format port " scm_puts (\">\", port);~%")
(format port " return 1;~%")
(format port "}~%")
;; Enum-to-string.
(format port "SCM_DEFINE (scm_gnutls_~a_to_string, \"~a->string\", "
subsystem (enum-type-subsystem enum))
(format port "1, 0, 0,~%")
(format port " (SCM enumval),~%")
(format port " \"Return a string describing ")
(format port "@var{enumval}, a @code{~a} value.\")~%"
(enum-type-subsystem enum))
(format port "#define FUNC_NAME s_scm_gnutls_~a_to_string~%"
subsystem)
(format port "{~%")
(format port " ~a c_enum;~%"
(enum-type-c-type enum))
(format port " const char *c_string;~%")
(format port " c_enum = ~a (enumval, 1, FUNC_NAME);~%"
(enum-type-to-c-function enum))
(format port " c_string = ~a (c_enum);~%"
get-name)
(format port " return (scm_from_locale_string (c_string));~%")
(format port "}~%")
(format port "#undef FUNC_NAME~%")))))
(define (output-enum-definitions enum port)
;; Output to PORT the Guile C code that defines the values of ENUM-ALIST.
(let ((subsystem (scheme-symbol->c-name (enum-type-subsystem enum))))
(format port " enum_values = SCM_EOL;~%")
(for-each (lambda (c+scheme)
(format port " SCM_NEWSMOB (enum_smob, ~a, "
(enum-type-smob-name enum))
(format port "(scm_t_bits) ~a);~%"
(car c+scheme))
(format port " enum_values = scm_cons (enum_smob, ")
(format port "enum_values);~%")
(format port " scm_c_define (\"~a\", enum_smob);~%"
(symbol-append (enum-type-subsystem enum) '/
(cdr c+scheme))))
(enum-type-value-alist enum))
(format port " ~a = scm_permanent_object (enum_values);~%"
(enum-type-smob-list enum))))
(define (output-enum-declarations enum port)
;; Issue header file declarations needed for the inline functions that
;; handle ENUM values.
(format port "SCM_API scm_t_bits ~a;~%"
(enum-type-smob-name enum))
(format port "SCM_API SCM ~a;~%"
(enum-type-smob-list enum)))
(define (output-enum-definition-function enums port)
;; Output a C function that does all the `scm_c_define ()' for the enums
;; listed in ENUMS.
(format port "static inline void~%scm_gnutls_define_enums (void)~%{~%")
(format port " SCM enum_values, enum_smob;~%")
(for-each (lambda (enum)
(output-enum-definitions enum port))
enums)
(format port "}~%"))
(define (output-c->enum-converter enum port)
;; Output a C->Scheme converted for ENUM. This works by walking the list
;; of available enum values (SMOBs) for ENUM and then returning the
;; matching SMOB, so that users can then compare enums using `eq?'. While
;; this may look inefficient, this shouldn't be a problem since (i)
;; conversion in that direction is rarely needed and (ii) the number of
;; values per enum is expected to be small.
(format port "static inline SCM~%~a (~a c_obj)~%{~%"
(enum-type-from-c-function enum)
(enum-type-c-type enum))
(format port " SCM pair, result = SCM_BOOL_F;~%")
(format port " for (pair = ~a; scm_is_pair (pair); "
(enum-type-smob-list enum))
(format port "pair = SCM_CDR (pair))~%")
(format port " {~%")
(format port " SCM enum_smob;~%")
(format port " enum_smob = SCM_CAR (pair);~%")
(format port " if ((~a) SCM_SMOB_DATA (enum_smob) == c_obj)~%"
(enum-type-c-type enum))
(format port " {~%")
(format port " result = enum_smob;~%")
(format port " break;~%")
(format port " }~%")
(format port " }~%")
(format port " return result;~%")
(format port "}~%"))
(define (output-enum->c-converter enum port)
(let* ((c-type-name (enum-type-c-type enum))
(subsystem (scheme-symbol->c-name (enum-type-subsystem enum))))
(format port
"static inline ~a~%~a (SCM obj, unsigned pos, const char *func)~%"
c-type-name (enum-type-to-c-function enum))
(format port "#define FUNC_NAME func~%")
(format port "{~%")
(format port " SCM_VALIDATE_SMOB (pos, obj, ~a);~%"
(string-append "gnutls_" subsystem "_enum"))
(format port " return ((~a) SCM_SMOB_DATA (obj));~%"
c-type-name)
(format port "}~%")
(format port "#undef FUNC_NAME~%")))
(define (output-enum-get-name-function enum port)
;; Output a C function that, when passed a C ENUM value, returns a C string
;; representing that value.
(let ((function (enum-type-automatic-get-name-function enum)))
(format port
"static const char *~%~a (~a c_obj)~%"
function (enum-type-c-type enum))
(format port "{~%")
(format port " static const struct ")
(format port "{ ~a value; const char *name; } "
(enum-type-c-type enum))
(format port "table[] =~%")
(format port " {~%")
(for-each (lambda (c+scheme)
(format port " { ~a, \"~a\" },~%"
(car c+scheme) (cdr c+scheme)))
(enum-type-value-alist enum))
(format port " };~%")
(format port " unsigned i;~%")
(format port " const char *name = NULL;~%")
(format port " for (i = 0; i < ~a; i++)~%"
(length (enum-type-value-alist enum)))
(format port " {~%")
(format port " if (table[i].value == c_obj)~%")
(format port " {~%")
(format port " name = table[i].name;~%")
(format port " break;~%")
(format port " }~%")
(format port " }~%")
(format port " return (name);~%")
(format port "}~%")))
;;;
;;; Actual enumerations.
;;;
(define %cipher-enum
(make-enum-type 'cipher "gnutls_cipher_algorithm_t"
'(null arcfour 3des-cbc aes-128-cbc aes-256-cbc
arcfour-40 rc2-40-cbc des-cbc)
"gnutls_cipher_get_name"))
(define %kx-enum
(make-enum-type 'kx "gnutls_kx_algorithm_t"
'(rsa dhe-dss dhe-rsa anon-dh srp rsa-export
srp-rsa srp-dss psk dhe-dss)
"gnutls_kx_get_name"))
(define %params-enum
(make-enum-type 'params "gnutls_params_type_t"
'(rsa-export dh)
#f))
(define %credentials-enum
(make-enum-type 'credentials "gnutls_credentials_type_t"
'(certificate anon srp psk ia)
#f
"GNUTLS_CRD_"))
(define %mac-enum
(make-enum-type 'mac "gnutls_mac_algorithm_t"
'(unknown null md5 sha1 rmd160 md2)
"gnutls_mac_get_name"))
(define %digest-enum
(make-enum-type 'digest "gnutls_digest_algorithm_t"
'(null md5 sha1 rmd160 md2)
#f
"GNUTLS_DIG_"))
(define %compression-method-enum
(make-enum-type 'compression-method "gnutls_compression_method_t"
'(null deflate lzo)
"gnutls_compression_get_name"
"GNUTLS_COMP_"))
(define %connection-end-enum
(make-enum-type 'connection-end "gnutls_connection_end_t"
'(server client)
#f
"GNUTLS_"))
(define %alert-level-enum
(make-enum-type 'alert-level "gnutls_alert_level_t"
'(warning fatal)
#f
"GNUTLS_AL_"))
(define %alert-description-enum
(make-enum-type 'alert-description "gnutls_alert_description_t"
'(close-notify unexpected-message bad-record-mac
decryption-failed record-overflow decompression-failure handshake-failure
ssl3-no-certificate bad-certificate unsupported-certificate
certificate-revoked certificate-expired certificate-unknown illegal-parameter
unknown-ca access-denied decode-error decrypt-error export-restriction
protocol-version insufficient-security internal-error user-canceled
no-renegotiation unsupported-extension certificate-unobtainable
unrecognized-name unknown-psk-identity
inner-application-failure inner-application-verification)
#f
"GNUTLS_A_"))
(define %handshake-description-enum
(make-enum-type 'handshake-description "gnutls_handshake_description_t"
'(hello-request client-hello server-hello certificate-pkt
server-key-exchange certificate-request server-hello-done
certificate-verify client-key-exchange finished)
#f
"GNUTLS_HANDSHAKE_"))
(define %certificate-status-enum
(make-enum-type 'certificate-status "gnutls_certificate_status_t"
'(invalid revoked signer-not-found signer-not-ca
insecure-algorithm)
#f
"GNUTLS_CERT_"))
(define %certificate-request-enum
(make-enum-type 'certificate-request "gnutls_certificate_request_t"
'(ignore request require)
#f
"GNUTLS_CERT_"))
;; XXX: Broken naming convention.
; (define %openpgp-key-status-enum
; (make-enum-type 'openpgp-key-status "gnutls_openpgp_key_status_t"
; '(key fingerprint)
; #f
; "GNUTLS_OPENPGP_"))
(define %close-request-enum
(make-enum-type 'close-request "gnutls_close_request_t"
'(rdwr wr) ;; FIXME: Check the meaning and rename
#f
"GNUTLS_SHUT_"))
(define %protocol-enum
(make-enum-type 'protocol "gnutls_protocol_t"
'(ssl3 tls1-0 tls1-1 version-unknown)
#f
"GNUTLS_"))
(define %certificate-type-enum
(make-enum-type 'certificate-type "gnutls_certificate_type_t"
'(x509 openpgp)
"gnutls_certificate_type_get_name"
"GNUTLS_CRT_"))
(define %x509-certificate-format-enum
(make-enum-type 'x509-certificate-format "gnutls_x509_crt_fmt_t"
'(der pem)
#f
"GNUTLS_X509_FMT_"))
(define %x509-subject-alternative-name-enum
(make-enum-type 'x509-subject-alternative-name
"gnutls_x509_subject_alt_name_t"
'(dnsname rfc822name uri ipaddress)
#f
"GNUTLS_SAN_"))
(define %pk-algorithm-enum
(make-enum-type 'pk-algorithm "gnutls_pk_algorithm_t"
'(unknown rsa dsa)
"gnutls_pk_algorithm_get_name"
"GNUTLS_PK_"))
(define %sign-algorithm-enum
(make-enum-type 'sign-algorithm "gnutls_sign_algorithm_t"
'(unknown rsa-sha1 dsa-sha1 rsa-md5 rsa-md2
rsa-rmd160)
"gnutls_sign_algorithm_get_name"
"GNUTLS_SIGN_"))
(define %psk-key-format-enum
(make-enum-type 'psk-key-format "gnutls_psk_key_flags"
'(raw hex)
#f
"GNUTLS_PSK_KEY_"))
(define %key-usage-enum
;; Not actually an enum on the C side.
(make-enum-type 'key-usage "int"
'(digital-signature non-repudiation key-encipherment
data-encipherment key-agreement key-cert-sign
crl-sign encipher-only decipher-only)
#f
"GNUTLS_KEY_"))
(define %certificate-verify-enum
(make-enum-type 'certificate-verify "gnutls_certificate_verify_flags"
'(disable-ca-sign allow-x509-v1-ca-crt
do-not-allow-same allow-any-x509-v1-ca-crt
allow-sign-rsa-md2 allow-sign-rsa-md5)
#f
"GNUTLS_VERIFY_"))
(define %error-enum
(make-enum-type 'error "int"
'(
success
unknown-compression-algorithm
unknown-cipher-type
large-packet
unsupported-version-packet
unexpected-packet-length
invalid-session
fatal-alert-received
unexpected-packet
warning-alert-received
error-in-finished-packet
unexpected-handshake-packet
unknown-cipher-suite
unwanted-algorithm
mpi-scan-failed
decryption-failed
memory-error
decompression-failed
compression-failed
again
expired
db-error
srp-pwd-error
insufficient-credentials
insuficient-credentials
insufficient-cred
insuficient-cred
hash-failed
base64-decoding-error
mpi-print-failed
rehandshake
got-application-data
record-limit-reached
encryption-failed
pk-encryption-failed
pk-decryption-failed
pk-sign-failed
x509-unsupported-critical-extension
key-usage-violation
no-certificate-found
invalid-request
short-memory-buffer
interrupted
push-error
pull-error
received-illegal-parameter
requested-data-not-available
pkcs1-wrong-pad
received-illegal-extension
internal-error
dh-prime-unacceptable
file-error
too-many-empty-packets
unknown-pk-algorithm
init-libextra
library-version-mismatch
no-temporary-rsa-params
lzo-init-failed
no-compression-algorithms
no-cipher-suites
openpgp-getkey-failed
pk-sig-verify-failed
illegal-srp-username
srp-pwd-parsing-error
no-temporary-dh-params
asn1-element-not-found
asn1-identifier-not-found
asn1-der-error
asn1-value-not-found
asn1-generic-error
asn1-value-not-valid
asn1-tag-error
asn1-tag-implicit
asn1-type-any-error
asn1-syntax-error
asn1-der-overflow
openpgp-uid-revoked
certificate-error
x509-certificate-error
certificate-key-mismatch
unsupported-certificate-type
x509-unknown-san
openpgp-fingerprint-unsupported
x509-unsupported-attribute
unknown-algorithm
unknown-hash-algorithm
unknown-pkcs-content-type
unknown-pkcs-bag-type
invalid-password
mac-verify-failed
constraint-error
warning-ia-iphf-received
warning-ia-fphf-received
ia-verify-failed
base64-encoding-error
incompatible-gcrypt-library
incompatible-crypto-library
incompatible-libtasn1-library
openpgp-keyring-error
x509-unsupported-oid
random-failed
unimplemented-feature)
"gnutls_strerror"
"GNUTLS_E_"))
(define %openpgp-certificate-format-enum
(make-enum-type 'openpgp-certificate-format "gnutls_openpgp_crt_fmt_t"
'(raw base64)
#f
"GNUTLS_OPENPGP_FMT_"))
(define %gnutls-enums
;; All enums.
(list %cipher-enum %kx-enum %params-enum %credentials-enum %mac-enum
%digest-enum %compression-method-enum %connection-end-enum
%alert-level-enum %alert-description-enum %handshake-description-enum
%certificate-status-enum %certificate-request-enum
%close-request-enum %protocol-enum %certificate-type-enum
%x509-certificate-format-enum %x509-subject-alternative-name-enum
%pk-algorithm-enum %sign-algorithm-enum
%psk-key-format-enum %key-usage-enum %certificate-verify-enum
%error-enum))
(define %gnutls-extra-enums
;; All enums for GnuTLS-extra (GPL).
(list %openpgp-certificate-format-enum))
;;; Local Variables:
;;; mode: scheme
;;; coding: latin-1
;;; End:
;;; arch-tag: 9e3eb6bb-61a5-4e85-861f-1914ab9677b0
|