110584
|
1 /* GnuTLS glue for GNU Emacs.
|
|
2 Copyright (C) 2010 Free Software Foundation, Inc.
|
|
3
|
|
4 This file is part of GNU Emacs.
|
|
5
|
|
6 GNU Emacs is free software: you can redistribute it and/or modify
|
|
7 it under the terms of the GNU General Public License as published by
|
|
8 the Free Software Foundation, either version 3 of the License, or
|
|
9 (at your option) any later version.
|
|
10
|
|
11 GNU Emacs is distributed in the hope that it will be useful,
|
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14 GNU General Public License for more details.
|
|
15
|
|
16 You should have received a copy of the GNU General Public License
|
|
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
|
18
|
|
19 #include <config.h>
|
|
20 #include <errno.h>
|
|
21 #include <setjmp.h>
|
|
22
|
|
23 #include "lisp.h"
|
|
24 #include "process.h"
|
|
25
|
|
26 #ifdef HAVE_GNUTLS
|
|
27 #include <gnutls/gnutls.h>
|
|
28
|
|
29 Lisp_Object Qgnutls_code;
|
|
30 Lisp_Object Qgnutls_anon, Qgnutls_x509pki;
|
|
31 Lisp_Object Qgnutls_e_interrupted, Qgnutls_e_again,
|
|
32 Qgnutls_e_invalid_session, Qgnutls_e_not_ready_for_handshake;
|
|
33 int global_initialized;
|
|
34
|
|
35 int
|
|
36 emacs_gnutls_write (int fildes, gnutls_session_t state, char *buf,
|
|
37 unsigned int nbyte)
|
|
38 {
|
|
39 register int rtnval, bytes_written;
|
|
40
|
|
41 bytes_written = 0;
|
|
42
|
|
43 while (nbyte > 0)
|
|
44 {
|
|
45 rtnval = gnutls_write (state, buf, nbyte);
|
|
46
|
|
47 if (rtnval == -1)
|
|
48 {
|
|
49 if (errno == EINTR)
|
|
50 continue;
|
|
51 else
|
|
52 return (bytes_written ? bytes_written : -1);
|
|
53 }
|
|
54
|
|
55 buf += rtnval;
|
|
56 nbyte -= rtnval;
|
|
57 bytes_written += rtnval;
|
|
58 }
|
|
59 fsync (STDOUT_FILENO);
|
|
60
|
|
61 return (bytes_written);
|
|
62 }
|
|
63
|
|
64 int
|
|
65 emacs_gnutls_read (int fildes, gnutls_session_t state, char *buf,
|
|
66 unsigned int nbyte)
|
|
67 {
|
|
68 register int rtnval;
|
|
69
|
|
70 do {
|
|
71 rtnval = gnutls_read (state, buf, nbyte);
|
|
72 } while (rtnval == GNUTLS_E_INTERRUPTED || rtnval == GNUTLS_E_AGAIN);
|
|
73 fsync (STDOUT_FILENO);
|
|
74
|
|
75 return (rtnval);
|
|
76 }
|
|
77
|
|
78 /* convert an integer error to a Lisp_Object; it will be either a
|
|
79 known symbol like `gnutls_e_interrupted' and `gnutls_e_again' or
|
|
80 simply the integer value of the error. GNUTLS_E_SUCCESS is mapped
|
|
81 to Qt. */
|
|
82 Lisp_Object gnutls_make_error (int error)
|
|
83 {
|
|
84 switch (error)
|
|
85 {
|
|
86 case GNUTLS_E_SUCCESS:
|
|
87 return Qt;
|
|
88 case GNUTLS_E_AGAIN:
|
|
89 return Qgnutls_e_again;
|
|
90 case GNUTLS_E_INTERRUPTED:
|
|
91 return Qgnutls_e_interrupted;
|
|
92 case GNUTLS_E_INVALID_SESSION:
|
|
93 return Qgnutls_e_invalid_session;
|
|
94 }
|
|
95
|
|
96 return make_number (error);
|
|
97 }
|
|
98
|
|
99 DEFUN ("gnutls-get-initstage", Fgnutls_get_initstage, Sgnutls_get_initstage, 1, 1, 0,
|
|
100 doc: /* Return the GnuTLS init stage of PROCESS.
|
|
101 See also `gnutls-boot'. */)
|
|
102 (Lisp_Object proc)
|
|
103 {
|
|
104 CHECK_PROCESS (proc);
|
|
105
|
|
106 return make_number (GNUTLS_INITSTAGE (proc));
|
|
107 }
|
|
108
|
|
109 DEFUN ("gnutls-errorp", Fgnutls_errorp, Sgnutls_errorp, 1, 1, 0,
|
|
110 doc: /* Returns t if ERROR (as generated by gnutls_make_error)
|
|
111 indicates a GnuTLS problem. */)
|
|
112 (Lisp_Object error)
|
|
113 {
|
|
114 if (EQ (error, Qt)) return Qnil;
|
|
115
|
|
116 return Qt;
|
|
117 }
|
|
118
|
|
119 DEFUN ("gnutls-error-fatalp", Fgnutls_error_fatalp, Sgnutls_error_fatalp, 1, 1, 0,
|
|
120 doc: /* Checks if ERROR is fatal.
|
|
121 ERROR is an integer or a symbol with an integer `gnutls-code' property. */)
|
|
122 (Lisp_Object err)
|
|
123 {
|
|
124 Lisp_Object code;
|
|
125
|
|
126 if (EQ (err, Qt)) return Qnil;
|
|
127
|
|
128 if (SYMBOLP (err))
|
|
129 {
|
|
130 code = Fget (err, Qgnutls_code);
|
|
131 if (NUMBERP (code))
|
|
132 {
|
|
133 err = code;
|
|
134 }
|
|
135 else
|
|
136 {
|
|
137 error ("Symbol has no numeric gnutls-code property");
|
|
138 }
|
|
139 }
|
|
140
|
|
141 if (!NUMBERP (err))
|
|
142 error ("Not an error symbol or code");
|
|
143
|
|
144 if (0 == gnutls_error_is_fatal (XINT (err)))
|
|
145 return Qnil;
|
|
146
|
|
147 return Qt;
|
|
148 }
|
|
149
|
|
150 DEFUN ("gnutls-error-string", Fgnutls_error_string, Sgnutls_error_string, 1, 1, 0,
|
|
151 doc: /* Returns a description of ERROR.
|
|
152 ERROR is an integer or a symbol with an integer `gnutls-code' property. */)
|
|
153 (Lisp_Object err)
|
|
154 {
|
|
155 Lisp_Object code;
|
|
156
|
|
157 if (EQ (err, Qt)) return build_string ("Not an error");
|
|
158
|
|
159 if (SYMBOLP (err))
|
|
160 {
|
|
161 code = Fget (err, Qgnutls_code);
|
|
162 if (NUMBERP (code))
|
|
163 {
|
|
164 err = code;
|
|
165 }
|
|
166 else
|
|
167 {
|
|
168 return build_string ("Symbol has no numeric gnutls-code property");
|
|
169 }
|
|
170 }
|
|
171
|
|
172 if (!NUMBERP (err))
|
|
173 return build_string ("Not an error symbol or code");
|
|
174
|
|
175 return build_string (gnutls_strerror (XINT (err)));
|
|
176 }
|
|
177
|
|
178 DEFUN ("gnutls-deinit", Fgnutls_deinit, Sgnutls_deinit, 1, 1, 0,
|
|
179 doc: /* Deallocate GNU TLS resources associated with PROCESS.
|
|
180 See also `gnutls-init'. */)
|
|
181 (Lisp_Object proc)
|
|
182 {
|
|
183 gnutls_session_t state;
|
|
184
|
|
185 CHECK_PROCESS (proc);
|
|
186 state = XPROCESS (proc)->gnutls_state;
|
|
187
|
|
188 if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_INIT)
|
|
189 {
|
|
190 gnutls_deinit (state);
|
|
191 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT - 1;
|
|
192 }
|
|
193
|
|
194 return Qt;
|
|
195 }
|
|
196
|
|
197 /* Initializes global GNU TLS state to defaults.
|
|
198 Call `gnutls-global-deinit' when GNU TLS usage is no longer needed.
|
|
199 Returns zero on success. */
|
|
200 Lisp_Object gnutls_emacs_global_init (void)
|
|
201 {
|
|
202 int ret = GNUTLS_E_SUCCESS;
|
|
203
|
|
204 if (!global_initialized)
|
|
205 ret = gnutls_global_init ();
|
|
206
|
|
207 global_initialized = 1;
|
|
208
|
|
209 return gnutls_make_error (ret);
|
|
210 }
|
|
211
|
|
212 /* Deinitializes global GNU TLS state.
|
|
213 See also `gnutls-global-init'. */
|
|
214 Lisp_Object gnutls_emacs_global_deinit (void)
|
|
215 {
|
|
216 if (global_initialized)
|
|
217 gnutls_global_deinit ();
|
|
218
|
|
219 global_initialized = 0;
|
|
220
|
|
221 return gnutls_make_error (GNUTLS_E_SUCCESS);
|
|
222 }
|
|
223
|
|
224 DEFUN ("gnutls-boot", Fgnutls_boot, Sgnutls_boot, 3, 6, 0,
|
|
225 doc: /* Initializes client-mode GnuTLS for process PROC.
|
|
226 Currently only client mode is supported. Returns a success/failure
|
|
227 value you can check with `gnutls-errorp'.
|
|
228
|
|
229 PRIORITY_STRING is a string describing the priority.
|
|
230 TYPE is either `gnutls-anon' or `gnutls-x509pki'.
|
|
231 TRUSTFILE is a PEM encoded trust file for `gnutls-x509pki'.
|
|
232 KEYFILE is ... for `gnutls-x509pki' (TODO).
|
|
233 CALLBACK is ... for `gnutls-x509pki' (TODO).
|
|
234
|
|
235 Note that the priority is set on the client. The server does not use
|
|
236 the protocols's priority except for disabling protocols that were not
|
|
237 specified.
|
|
238
|
|
239 Processes must be initialized with this function before other GNU TLS
|
|
240 functions are used. This function allocates resources which can only
|
|
241 be deallocated by calling `gnutls-deinit' or by calling it again.
|
|
242
|
|
243 Each authentication type may need additional information in order to
|
|
244 work. For X.509 PKI (`gnutls-x509pki'), you need TRUSTFILE and
|
|
245 KEYFILE and optionally CALLBACK. */)
|
|
246 (Lisp_Object proc, Lisp_Object priority_string, Lisp_Object type,
|
|
247 Lisp_Object trustfile, Lisp_Object keyfile, Lisp_Object callback)
|
|
248 {
|
|
249 int ret = GNUTLS_E_SUCCESS;
|
|
250
|
|
251 /* TODO: GNUTLS_X509_FMT_DER is also an option. */
|
|
252 int file_format = GNUTLS_X509_FMT_PEM;
|
|
253
|
|
254 gnutls_session_t state;
|
|
255 gnutls_certificate_credentials_t x509_cred;
|
|
256 gnutls_anon_client_credentials_t anon_cred;
|
|
257 gnutls_srp_client_credentials_t srp_cred;
|
|
258 gnutls_datum_t data;
|
|
259 Lisp_Object global_init;
|
|
260
|
|
261 CHECK_PROCESS (proc);
|
|
262 CHECK_SYMBOL (type);
|
|
263 CHECK_STRING (priority_string);
|
|
264
|
|
265 state = XPROCESS (proc)->gnutls_state;
|
|
266
|
|
267 /* always initialize globals. */
|
|
268 global_init = gnutls_emacs_global_init ();
|
|
269 if (! NILP (Fgnutls_errorp (global_init)))
|
|
270 return global_init;
|
|
271
|
|
272 /* deinit and free resources. */
|
|
273 if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_CRED_ALLOC)
|
|
274 {
|
|
275 message ("gnutls: deallocating certificates");
|
|
276
|
|
277 if (EQ (type, Qgnutls_x509pki))
|
|
278 {
|
|
279 message ("gnutls: deallocating x509 certificates");
|
|
280
|
|
281 x509_cred = XPROCESS (proc)->x509_cred;
|
|
282 gnutls_certificate_free_credentials (x509_cred);
|
|
283 }
|
|
284 else if (EQ (type, Qgnutls_anon))
|
|
285 {
|
|
286 message ("gnutls: deallocating anon certificates");
|
|
287
|
|
288 anon_cred = XPROCESS (proc)->anon_cred;
|
|
289 gnutls_anon_free_client_credentials (anon_cred);
|
|
290 }
|
|
291 else
|
|
292 {
|
|
293 error ("unknown credential type");
|
|
294 ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
|
|
295 }
|
|
296
|
|
297 if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_INIT)
|
|
298 {
|
|
299 message ("gnutls: deinitializing");
|
|
300
|
|
301 Fgnutls_deinit (proc);
|
|
302 }
|
|
303 }
|
|
304
|
|
305 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_EMPTY;
|
|
306
|
|
307 message ("gnutls: allocating credentials");
|
|
308
|
|
309 if (EQ (type, Qgnutls_x509pki))
|
|
310 {
|
|
311 message ("gnutls: allocating x509 credentials");
|
|
312
|
|
313 x509_cred = XPROCESS (proc)->x509_cred;
|
|
314 if (gnutls_certificate_allocate_credentials (&x509_cred) < 0)
|
|
315 memory_full ();
|
|
316 }
|
|
317 else if (EQ (type, Qgnutls_anon))
|
|
318 {
|
|
319 message ("gnutls: allocating anon credentials");
|
|
320
|
|
321 anon_cred = XPROCESS (proc)->anon_cred;
|
|
322 if (gnutls_anon_allocate_client_credentials (&anon_cred) < 0)
|
|
323 memory_full ();
|
|
324 }
|
|
325 else
|
|
326 {
|
|
327 error ("unknown credential type");
|
|
328 ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
|
|
329 }
|
|
330
|
|
331 if (ret < GNUTLS_E_SUCCESS)
|
|
332 return gnutls_make_error (ret);
|
|
333
|
|
334 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_CRED_ALLOC;
|
|
335
|
|
336 message ("gnutls: setting the trustfile");
|
|
337
|
|
338 if (EQ (type, Qgnutls_x509pki))
|
|
339 {
|
|
340 if (STRINGP (trustfile))
|
|
341 {
|
|
342 ret = gnutls_certificate_set_x509_trust_file
|
|
343 (x509_cred,
|
|
344 XSTRING (trustfile)->data,
|
|
345 file_format);
|
|
346
|
|
347 if (ret < GNUTLS_E_SUCCESS)
|
|
348 return gnutls_make_error (ret);
|
|
349
|
|
350 message ("gnutls: processed %d CA certificates", ret);
|
|
351 }
|
|
352
|
|
353 message ("gnutls: setting the keyfile");
|
|
354
|
|
355 if (STRINGP (keyfile))
|
|
356 {
|
|
357 ret = gnutls_certificate_set_x509_crl_file
|
|
358 (x509_cred,
|
|
359 XSTRING (keyfile)->data,
|
|
360 file_format);
|
|
361
|
|
362 if (ret < GNUTLS_E_SUCCESS)
|
|
363 return gnutls_make_error (ret);
|
|
364
|
|
365 message ("gnutls: processed %d CRL(s)", ret);
|
|
366 }
|
|
367 }
|
|
368
|
|
369 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_FILES;
|
|
370
|
|
371 message ("gnutls: gnutls_init");
|
|
372
|
|
373 ret = gnutls_init (&state, GNUTLS_CLIENT);
|
|
374
|
|
375 if (ret < GNUTLS_E_SUCCESS)
|
|
376 return gnutls_make_error (ret);
|
|
377
|
|
378 XPROCESS (proc)->gnutls_state = state;
|
|
379
|
|
380 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT;
|
|
381
|
|
382 message ("gnutls: setting the priority string");
|
|
383
|
|
384 ret = gnutls_priority_set_direct(state,
|
|
385 (char*) SDATA (priority_string),
|
|
386 NULL);
|
|
387
|
|
388 if (ret < GNUTLS_E_SUCCESS)
|
|
389 return gnutls_make_error (ret);
|
|
390
|
|
391 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_PRIORITY;
|
|
392
|
|
393 message ("gnutls: setting the credentials");
|
|
394
|
|
395 if (EQ (type, Qgnutls_x509pki))
|
|
396 {
|
|
397 message ("gnutls: setting the x509 credentials");
|
|
398
|
|
399 ret = gnutls_cred_set (state, GNUTLS_CRD_CERTIFICATE, x509_cred);
|
|
400 }
|
|
401 else if (EQ (type, Qgnutls_anon))
|
|
402 {
|
|
403 message ("gnutls: setting the anon credentials");
|
|
404
|
|
405 ret = gnutls_cred_set (state, GNUTLS_CRD_ANON, anon_cred);
|
|
406 }
|
|
407 else
|
|
408 {
|
|
409 error ("unknown credential type");
|
|
410 ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
|
|
411 }
|
|
412
|
|
413 if (ret < GNUTLS_E_SUCCESS)
|
|
414 return gnutls_make_error (ret);
|
|
415
|
|
416 XPROCESS (proc)->anon_cred = anon_cred;
|
|
417 XPROCESS (proc)->x509_cred = x509_cred;
|
|
418 XPROCESS (proc)->gnutls_cred_type = type;
|
|
419
|
|
420 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_CRED_SET;
|
|
421
|
|
422 return gnutls_make_error (GNUTLS_E_SUCCESS);
|
|
423 }
|
|
424
|
|
425 DEFUN ("gnutls-bye", Fgnutls_bye,
|
|
426 Sgnutls_bye, 2, 2, 0,
|
|
427 doc: /* Terminate current GNU TLS connection for PROCESS.
|
|
428 The connection should have been initiated using `gnutls-handshake'.
|
|
429
|
|
430 If CONT is not nil the TLS connection gets terminated and further
|
|
431 receives and sends will be disallowed. If the return value is zero you
|
|
432 may continue using the connection. If CONT is nil, GnuTLS actually
|
|
433 sends an alert containing a close request and waits for the peer to
|
|
434 reply with the same message. In order to reuse the connection you
|
|
435 should wait for an EOF from the peer.
|
|
436
|
|
437 This function may also return `gnutls-e-again', or
|
|
438 `gnutls-e-interrupted'. */)
|
|
439 (Lisp_Object proc, Lisp_Object cont)
|
|
440 {
|
|
441 gnutls_session_t state;
|
|
442 int ret;
|
|
443
|
|
444 CHECK_PROCESS (proc);
|
|
445
|
|
446 state = XPROCESS (proc)->gnutls_state;
|
|
447
|
|
448 ret = gnutls_bye (state,
|
|
449 NILP (cont) ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
|
|
450
|
|
451 return gnutls_make_error (ret);
|
|
452 }
|
|
453
|
|
454 DEFUN ("gnutls-handshake", Fgnutls_handshake,
|
|
455 Sgnutls_handshake, 1, 1, 0,
|
|
456 doc: /* Perform GNU TLS handshake for PROCESS.
|
|
457 The identity of the peer is checked automatically. This function will
|
|
458 fail if any problem is encountered, and will return a negative error
|
|
459 code. In case of a client, if it has been asked to resume a session,
|
|
460 but the server didn't, then a full handshake will be performed.
|
|
461
|
|
462 If the error `gnutls-e-not-ready-for-handshake' is returned, you
|
|
463 didn't call `gnutls-boot' first.
|
|
464
|
|
465 This function may also return the non-fatal errors `gnutls-e-again',
|
|
466 or `gnutls-e-interrupted'. In that case you may resume the handshake
|
|
467 (by calling this function again). */)
|
|
468 (Lisp_Object proc)
|
|
469 {
|
|
470 gnutls_session_t state;
|
|
471 int ret;
|
|
472
|
|
473 CHECK_PROCESS (proc);
|
|
474 state = XPROCESS (proc)->gnutls_state;
|
|
475
|
|
476 if (GNUTLS_INITSTAGE (proc) < GNUTLS_STAGE_HANDSHAKE_CANDO)
|
|
477 return Qgnutls_e_not_ready_for_handshake;
|
|
478
|
|
479
|
|
480 if (GNUTLS_INITSTAGE (proc) < GNUTLS_STAGE_TRANSPORT_POINTERS_SET)
|
|
481 {
|
|
482 /* for a network process in Emacs infd and outfd are the same
|
|
483 but this shows our intent more clearly. */
|
|
484 message ("gnutls: handshake: setting the transport pointers to %d/%d",
|
|
485 XPROCESS (proc)->infd, XPROCESS (proc)->outfd);
|
|
486
|
|
487 gnutls_transport_set_ptr2 (state, XPROCESS (proc)->infd,
|
|
488 XPROCESS (proc)->outfd);
|
|
489
|
|
490 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_TRANSPORT_POINTERS_SET;
|
|
491 }
|
|
492
|
|
493 message ("gnutls: handshake: handshaking");
|
|
494 ret = gnutls_handshake (state);
|
|
495
|
|
496 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_HANDSHAKE_TRIED;
|
|
497
|
|
498 if (GNUTLS_E_SUCCESS == ret)
|
|
499 {
|
|
500 /* here we're finally done. */
|
|
501 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_READY;
|
|
502 }
|
|
503
|
|
504 return gnutls_make_error (ret);
|
|
505 }
|
|
506
|
|
507 void
|
|
508 syms_of_gnutls (void)
|
|
509 {
|
|
510 global_initialized = 0;
|
|
511
|
|
512 Qgnutls_code = intern_c_string ("gnutls-code");
|
|
513 staticpro (&Qgnutls_code);
|
|
514
|
|
515 Qgnutls_anon = intern_c_string ("gnutls-anon");
|
|
516 staticpro (&Qgnutls_anon);
|
|
517
|
|
518 Qgnutls_x509pki = intern_c_string ("gnutls-x509pki");
|
|
519 staticpro (&Qgnutls_x509pki);
|
|
520
|
|
521 Qgnutls_e_interrupted = intern_c_string ("gnutls-e-interrupted");
|
|
522 staticpro (&Qgnutls_e_interrupted);
|
|
523 Fput (Qgnutls_e_interrupted, Qgnutls_code,
|
|
524 make_number (GNUTLS_E_INTERRUPTED));
|
|
525
|
|
526 Qgnutls_e_again = intern_c_string ("gnutls-e-again");
|
|
527 staticpro (&Qgnutls_e_again);
|
|
528 Fput (Qgnutls_e_again, Qgnutls_code,
|
|
529 make_number (GNUTLS_E_AGAIN));
|
|
530
|
|
531 Qgnutls_e_invalid_session = intern_c_string ("gnutls-e-invalid-session");
|
|
532 staticpro (&Qgnutls_e_invalid_session);
|
|
533 Fput (Qgnutls_e_invalid_session, Qgnutls_code,
|
|
534 make_number (GNUTLS_E_INVALID_SESSION));
|
|
535
|
|
536 Qgnutls_e_not_ready_for_handshake =
|
|
537 intern_c_string ("gnutls-e-not-ready-for-handshake");
|
|
538 staticpro (&Qgnutls_e_not_ready_for_handshake);
|
|
539 Fput (Qgnutls_e_not_ready_for_handshake, Qgnutls_code,
|
|
540 make_number (GNUTLS_E_APPLICATION_ERROR_MIN));
|
|
541
|
|
542 defsubr (&Sgnutls_get_initstage);
|
|
543 defsubr (&Sgnutls_errorp);
|
|
544 defsubr (&Sgnutls_error_fatalp);
|
|
545 defsubr (&Sgnutls_error_string);
|
|
546 defsubr (&Sgnutls_boot);
|
|
547 defsubr (&Sgnutls_deinit);
|
|
548 defsubr (&Sgnutls_handshake);
|
|
549 defsubr (&Sgnutls_bye);
|
|
550 }
|
|
551 #endif
|