ldb:pyldb.h - revert to the previous header behaviour
[kai/samba.git] / source4 / libnet / py_net.c
1 /*
2    Unix SMB/CIFS implementation.
3    Samba utility functions
4    Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008-2010
5    Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <Python.h>
22 #include "includes.h"
23 #include <ldb.h>
24 #include <pyldb.h>
25 #include "libnet.h"
26 #include "auth/credentials/pycredentials.h"
27 #include "libcli/security/security.h"
28 #include "lib/events/events.h"
29 #include "param/pyparam.h"
30 #include "auth/gensec/gensec.h"
31 #include "librpc/rpc/pyrpc_util.h"
32 #include "libcli/finddc.h"
33 #include "libcli/resolve/resolve.h"
34
35 typedef struct {
36         PyObject_HEAD
37         TALLOC_CTX *mem_ctx;
38         struct libnet_context *libnet_ctx;
39         struct tevent_context *ev;
40 } py_net_Object;
41
42 static PyObject *py_net_join(py_net_Object *self, PyObject *args, PyObject *kwargs)
43 {
44         struct libnet_Join r;
45         NTSTATUS status;
46         PyObject *result;
47         TALLOC_CTX *mem_ctx;
48         const char *kwnames[] = { "domain_name", "netbios_name", "join_type", "level", NULL };
49
50         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssii:Join", discard_const_p(char *, kwnames), 
51                                          &r.in.domain_name, &r.in.netbios_name, 
52                                          &r.in.join_type, &r.in.level))
53                 return NULL;
54
55         mem_ctx = talloc_new(self->mem_ctx);
56         if (mem_ctx == NULL) {
57                 PyErr_NoMemory();
58                 return NULL;
59         }
60
61         status = libnet_Join(self->libnet_ctx, mem_ctx, &r);
62         if (NT_STATUS_IS_ERR(status)) {
63                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
64                 talloc_free(mem_ctx);
65                 return NULL;
66         }
67
68         result = Py_BuildValue("sss", r.out.join_password,
69                                dom_sid_string(mem_ctx, r.out.domain_sid),
70                                r.out.domain_name);
71
72         talloc_free(mem_ctx);
73
74         return result;
75 }
76
77 static const char py_net_join_doc[] = "join(domain_name, netbios_name, join_type, level) -> (join_password, domain_sid, domain_name)\n\n" \
78 "Join the domain with the specified name.";
79
80 static PyObject *py_net_set_password(py_net_Object *self, PyObject *args, PyObject *kwargs)
81 {
82         union libnet_SetPassword r;
83         NTSTATUS status;
84         PyObject *py_creds;
85         TALLOC_CTX *mem_ctx;
86         struct tevent_context *ev;
87         const char *kwnames[] = { "account_name", "domain_name", "newpassword", "credentials", NULL };
88
89         r.generic.level = LIBNET_SET_PASSWORD_GENERIC;
90
91         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sssO:set_password", discard_const_p(char *, kwnames),
92                                          &r.generic.in.account_name, &r.generic.in.domain_name,
93                                          &r.generic.in.newpassword, &py_creds)) {
94                 return NULL;
95         }
96
97         /* FIXME: we really need to get a context from the caller or we may end
98          * up with 2 event contexts */
99         ev = s4_event_context_init(NULL);
100
101         mem_ctx = talloc_new(ev);
102         if (mem_ctx == NULL) {
103                 PyErr_NoMemory();
104                 return NULL;
105         }
106
107         status = libnet_SetPassword(self->libnet_ctx, mem_ctx, &r);
108         if (NT_STATUS_IS_ERR(status)) {
109                 PyErr_SetString(PyExc_RuntimeError,
110                                 r.generic.out.error_string?r.generic.out.error_string:nt_errstr(status));
111                 talloc_free(mem_ctx);
112                 return NULL;
113         }
114
115         talloc_free(mem_ctx);
116
117         Py_RETURN_NONE;
118 }
119
120 static const char py_net_set_password_doc[] = "set_password(account_name, domain_name, newpassword) -> True\n\n" \
121 "Set password for a user. You must supply credential with enough rights to do this.\n\n" \
122 "Sample usage is:\n" \
123 "net.set_password(account_name=<account_name>,\n" \
124 "                domain_name=domain_name,\n" \
125 "                newpassword=new_pass)\n";
126
127
128 static PyObject *py_net_export_keytab(py_net_Object *self, PyObject *args, PyObject *kwargs)
129 {
130         struct libnet_export_keytab r;
131         TALLOC_CTX *mem_ctx;
132         const char *kwnames[] = { "keytab", NULL };
133         NTSTATUS status;
134
135         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:export_keytab", discard_const_p(char *, kwnames),
136                                          &r.in.keytab_name)) {
137                 return NULL;
138         }
139
140         mem_ctx = talloc_new(self->mem_ctx);
141         if (mem_ctx == NULL) {
142                 PyErr_NoMemory();
143                 return NULL;
144         }
145
146         status = libnet_export_keytab(self->libnet_ctx, mem_ctx, &r);
147         if (NT_STATUS_IS_ERR(status)) {
148                 PyErr_SetString(PyExc_RuntimeError,
149                                 r.out.error_string?r.out.error_string:nt_errstr(status));
150                 talloc_free(mem_ctx);
151                 return NULL;
152         }
153
154         talloc_free(mem_ctx);
155
156         Py_RETURN_NONE;
157 }
158
159 static const char py_net_export_keytab_doc[] = "export_keytab(keytab, name)\n\n"
160 "Export the DC keytab to a keytab file.";
161
162 static PyObject *py_net_time(py_net_Object *self, PyObject *args, PyObject *kwargs)
163 {
164         const char *kwnames[] = { "server_name", NULL };
165         union libnet_RemoteTOD r;
166         NTSTATUS status;
167         TALLOC_CTX *mem_ctx;
168         char timestr[64];
169         PyObject *ret;
170         struct tm *tm;
171
172         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s",
173                 discard_const_p(char *, kwnames), &r.generic.in.server_name))
174                 return NULL;
175
176         r.generic.level                 = LIBNET_REMOTE_TOD_GENERIC;
177
178         mem_ctx = talloc_new(NULL);
179         if (mem_ctx == NULL) {
180                 PyErr_NoMemory();
181                 return NULL;
182         }
183
184         status = libnet_RemoteTOD(self->libnet_ctx, mem_ctx, &r);
185         if (!NT_STATUS_IS_OK(status)) {
186                 PyErr_SetString(PyExc_RuntimeError,
187                                 r.generic.out.error_string?r.generic.out.error_string:nt_errstr(status));
188                 talloc_free(mem_ctx);
189                 return NULL;
190         }
191
192         ZERO_STRUCT(timestr);
193         tm = localtime(&r.generic.out.time);
194         strftime(timestr, sizeof(timestr)-1, "%c %Z",tm);
195         
196         ret = PyString_FromString(timestr);
197
198         talloc_free(mem_ctx);
199
200         return ret;
201 }
202
203 static const char py_net_time_doc[] = "time(server_name) -> timestr\n"
204 "Retrieve the remote time on a server";
205
206 static PyObject *py_net_user_create(py_net_Object *self, PyObject *args, PyObject *kwargs)
207 {
208         const char *kwnames[] = { "username", NULL };
209         NTSTATUS status;
210         TALLOC_CTX *mem_ctx;
211         struct libnet_CreateUser r;
212
213         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames), 
214                                                                          &r.in.user_name))
215                 return NULL;
216
217         r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
218
219         mem_ctx = talloc_new(NULL);
220         if (mem_ctx == NULL) {
221                 PyErr_NoMemory();
222                 return NULL;
223         }
224
225         status = libnet_CreateUser(self->libnet_ctx, mem_ctx, &r);
226         if (!NT_STATUS_IS_OK(status)) {
227                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
228                 talloc_free(mem_ctx);
229                 return NULL;
230         }
231
232         talloc_free(mem_ctx);
233         
234         Py_RETURN_NONE;
235 }
236
237 static const char py_net_create_user_doc[] = "create_user(username)\n"
238 "Create a new user.";
239
240 static PyObject *py_net_user_delete(py_net_Object *self, PyObject *args, PyObject *kwargs)
241 {
242         const char *kwnames[] = { "username", NULL };
243         NTSTATUS status;
244         TALLOC_CTX *mem_ctx;
245         struct libnet_DeleteUser r;
246
247         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames), 
248                                                                          &r.in.user_name))
249                 return NULL;
250
251         r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred);
252
253         mem_ctx = talloc_new(NULL);
254         if (mem_ctx == NULL) {
255                 PyErr_NoMemory();
256                 return NULL;
257         }
258
259         status = libnet_DeleteUser(self->libnet_ctx, mem_ctx, &r);
260         if (!NT_STATUS_IS_OK(status)) {
261                 PyErr_SetString(PyExc_RuntimeError, r.out.error_string?r.out.error_string:nt_errstr(status));
262                 talloc_free(mem_ctx);
263                 return NULL;
264         }
265
266         talloc_free(mem_ctx);
267         
268         Py_RETURN_NONE;
269 }
270
271 static const char py_net_delete_user_doc[] = "delete_user(username)\n"
272 "Delete a user.";
273
274 static PyObject *py_dom_sid_FromSid(struct dom_sid *sid)
275 {
276         PyObject *mod_security, *dom_sid_Type;
277
278         mod_security = PyImport_ImportModule("samba.dcerpc.security");
279         if (mod_security == NULL)
280                 return NULL;
281
282         dom_sid_Type = PyObject_GetAttrString(mod_security, "dom_sid");
283         if (dom_sid_Type == NULL)
284                 return NULL;
285
286         return py_talloc_reference((PyTypeObject *)dom_sid_Type, sid);
287 }
288
289 static PyObject *py_net_vampire(py_net_Object *self, PyObject *args, PyObject *kwargs)
290 {
291         const char *kwnames[] = { "domain", "target_dir", NULL };
292         NTSTATUS status;
293         TALLOC_CTX *mem_ctx;
294         PyObject *ret;
295         struct libnet_Vampire r;
296
297         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|z", discard_const_p(char *, kwnames),
298                                          &r.in.domain_name, &r.in.targetdir)) {
299                 return NULL;
300         }
301
302         r.in.netbios_name  = lpcfg_netbios_name(self->libnet_ctx->lp_ctx);
303         r.out.error_string = NULL;
304
305         mem_ctx = talloc_new(NULL);
306         if (mem_ctx == NULL) {
307                 PyErr_NoMemory();
308                 return NULL;
309         }
310
311         status = libnet_Vampire(self->libnet_ctx, mem_ctx, &r);
312
313         if (!NT_STATUS_IS_OK(status)) {
314                 PyErr_SetString(PyExc_RuntimeError,
315                                 r.out.error_string ? r.out.error_string : nt_errstr(status));
316                 talloc_free(mem_ctx);
317                 return NULL;
318         }
319
320         ret = Py_BuildValue("(sO)", r.out.domain_name, py_dom_sid_FromSid(r.out.domain_sid));
321
322         talloc_free(mem_ctx);
323
324         return ret;
325 }
326
327 struct replicate_state {
328         void *vampire_state;
329         dcerpc_InterfaceObject *drs_pipe;
330         struct libnet_BecomeDC_StoreChunk chunk;
331         DATA_BLOB gensec_skey;
332         struct libnet_BecomeDC_Partition partition;
333         struct libnet_BecomeDC_Forest forest;
334         struct libnet_BecomeDC_DestDSA dest_dsa;
335 };
336
337 /*
338   setup for replicate_chunk() calls
339  */
340 static PyObject *py_net_replicate_init(py_net_Object *self, PyObject *args, PyObject *kwargs)
341 {
342         const char *kwnames[] = { "samdb", "lp", "drspipe", NULL };
343         PyObject *py_ldb, *py_lp, *py_drspipe;
344         struct ldb_context *samdb;
345         struct loadparm_context *lp;
346         struct replicate_state *s;
347         NTSTATUS status;
348
349         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO",
350                                          discard_const_p(char *, kwnames),
351                                          &py_ldb, &py_lp, &py_drspipe)) {
352                 return NULL;
353         }
354
355         s = talloc_zero(NULL, struct replicate_state);
356         if (!s) return NULL;
357
358         lp = lpcfg_from_py_object(s, py_lp);
359         if (lp == NULL) {
360                 PyErr_SetString(PyExc_TypeError, "Expected lp object");
361                 talloc_free(s);
362                 return NULL;
363         }
364
365         samdb = PyLdb_AsLdbContext(py_ldb);
366         if (samdb == NULL) {
367                 PyErr_SetString(PyExc_TypeError, "Expected ldb object");
368                 talloc_free(s);
369                 return NULL;
370         }
371
372         s->drs_pipe = (dcerpc_InterfaceObject *)(py_drspipe);
373
374         s->vampire_state = libnet_vampire_replicate_init(s, samdb, lp);
375         if (s->vampire_state == NULL) {
376                 PyErr_SetString(PyExc_TypeError, "Failed to initialise vampire_state");
377                 talloc_free(s);
378                 return NULL;
379         }
380
381         status = gensec_session_key(s->drs_pipe->pipe->conn->security_state.generic_state,
382                                     &s->gensec_skey);
383         if (!NT_STATUS_IS_OK(status)) {
384                 PyErr_Format(PyExc_RuntimeError, "Unable to get session key from drspipe: %s",
385                              nt_errstr(status));
386                 talloc_free(s);
387                 return NULL;
388         }
389
390         s->forest.dns_name = lpcfg_dnsdomain(lp);
391
392         s->chunk.gensec_skey = &s->gensec_skey;
393         s->chunk.partition = &s->partition;
394         s->chunk.forest = &s->forest;
395         s->chunk.dest_dsa = &s->dest_dsa;
396
397         return PyCObject_FromTallocPtr(s);
398 }
399
400
401 /*
402   process one replication chunk
403  */
404 static PyObject *py_net_replicate_chunk(py_net_Object *self, PyObject *args, PyObject *kwargs)
405 {
406         const char *kwnames[] = { "state", "level", "ctr", "schema", NULL };
407         PyObject *py_state, *py_ctr, *py_schema;
408         struct replicate_state *s;
409         unsigned level;
410         NTSTATUS (*chunk_handler)(void *private_data, const struct libnet_BecomeDC_StoreChunk *c);
411         NTSTATUS status;
412
413         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OIO|O",
414                                          discard_const_p(char *, kwnames),
415                                          &py_state, &level, &py_ctr, &py_schema)) {
416                 return NULL;
417         }
418
419         s = talloc_get_type(PyCObject_AsVoidPtr(py_state), struct replicate_state);
420         if (!s) {
421                 PyErr_SetString(PyExc_TypeError, "Expected replication_state");
422                 return NULL;
423         }
424
425         switch (level) {
426         case 1:
427                 if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr1")) {
428                         return NULL;
429                 }
430                 s->chunk.ctr1                         = py_talloc_get_ptr(py_ctr);
431                 s->partition.nc                       = *s->chunk.ctr1->naming_context;
432                 s->partition.more_data                = s->chunk.ctr1->more_data;
433                 s->partition.source_dsa_guid          = s->chunk.ctr1->source_dsa_guid;
434                 s->partition.source_dsa_invocation_id = s->chunk.ctr1->source_dsa_invocation_id;
435                 s->partition.highwatermark            = s->chunk.ctr1->new_highwatermark;
436                 break;
437         case 6:
438                 if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr6")) {
439                         return NULL;
440                 }
441                 s->chunk.ctr6                         = py_talloc_get_ptr(py_ctr);
442                 s->partition.nc                       = *s->chunk.ctr6->naming_context;
443                 s->partition.more_data                = s->chunk.ctr6->more_data;
444                 s->partition.source_dsa_guid          = s->chunk.ctr6->source_dsa_guid;
445                 s->partition.source_dsa_invocation_id = s->chunk.ctr6->source_dsa_invocation_id;
446                 s->partition.highwatermark            = s->chunk.ctr6->new_highwatermark;
447                 break;
448         default:
449                 PyErr_Format(PyExc_TypeError, "Bad level %u in replicate_chunk", level);
450                 return NULL;
451         }
452
453         chunk_handler = libnet_vampire_cb_store_chunk;
454         if (py_schema) {
455                 if (!PyBool_Check(py_schema)) {
456                         PyErr_SetString(PyExc_TypeError, "Expected boolean schema");
457                         return NULL;
458                 }
459                 if (py_schema == Py_True) {
460                         chunk_handler = libnet_vampire_cb_schema_chunk;
461                 }
462         }
463
464         s->chunk.ctr_level = level;
465
466         status = chunk_handler(s->vampire_state, &s->chunk);
467         if (!NT_STATUS_IS_OK(status)) {
468                 PyErr_Format(PyExc_TypeError, "Failed to process chunk: %s", nt_errstr(status));
469                 return NULL;
470         }
471
472         Py_RETURN_NONE;
473 }
474
475
476 /*
477   find a DC given a domain name and server type
478  */
479 static PyObject *py_net_finddc(py_net_Object *self, PyObject *args)
480 {
481         const char *domain_name;
482         unsigned server_type;
483         NTSTATUS status;
484         struct finddcs *io;
485         TALLOC_CTX *mem_ctx;
486         PyObject *ret;
487
488         if (!PyArg_ParseTuple(args, "sI", &domain_name, &server_type)) {
489                 return NULL;
490         }
491
492         mem_ctx = talloc_new(self->mem_ctx);
493
494         io = talloc_zero(mem_ctx, struct finddcs);
495         io->in.domain_name = domain_name;
496         io->in.minimum_dc_flags = server_type;
497
498         status = finddcs_cldap(io, io,
499                                lpcfg_resolve_context(self->libnet_ctx->lp_ctx), self->ev);
500         if (NT_STATUS_IS_ERR(status)) {
501                 PyErr_SetString(PyExc_RuntimeError, nt_errstr(status));
502                 talloc_free(mem_ctx);
503                 return NULL;
504         }
505
506         ret = py_return_ndr_struct("samba.dcerpc.nbt", "NETLOGON_SAM_LOGON_RESPONSE_EX",
507                                    io, &io->out.netlogon.data.nt5_ex);
508         talloc_free(mem_ctx);
509
510         return ret;
511 }
512
513
514 static const char py_net_vampire_doc[] = "vampire(domain, target_dir=None)\n"
515                                          "Vampire a domain.";
516
517 static const char py_net_replicate_init_doc[] = "replicate_init(samdb, lp, drspipe)\n"
518                                          "Setup for replicate_chunk calls.";
519
520 static const char py_net_replicate_chunk_doc[] = "replicate_chunk(state, level, ctr, schema)\n"
521                                          "Process replication for one chunk";
522
523 static const char py_net_finddc_doc[] = "finddc(domain, server_type)\n"
524                                          "find a DC with the specified server_type bits. Return the DNS name";
525
526 static PyMethodDef net_obj_methods[] = {
527         {"join", (PyCFunction)py_net_join, METH_VARARGS|METH_KEYWORDS, py_net_join_doc},
528         {"set_password", (PyCFunction)py_net_set_password, METH_VARARGS|METH_KEYWORDS, py_net_set_password_doc},
529         {"export_keytab", (PyCFunction)py_net_export_keytab, METH_VARARGS|METH_KEYWORDS, py_net_export_keytab_doc},
530         {"time", (PyCFunction)py_net_time, METH_VARARGS|METH_KEYWORDS, py_net_time_doc},
531         {"create_user", (PyCFunction)py_net_user_create, METH_VARARGS|METH_KEYWORDS, py_net_create_user_doc},
532         {"delete_user", (PyCFunction)py_net_user_delete, METH_VARARGS|METH_KEYWORDS, py_net_delete_user_doc},
533         {"vampire", (PyCFunction)py_net_vampire, METH_VARARGS|METH_KEYWORDS, py_net_vampire_doc},
534         {"replicate_init", (PyCFunction)py_net_replicate_init, METH_VARARGS|METH_KEYWORDS, py_net_replicate_init_doc},
535         {"replicate_chunk", (PyCFunction)py_net_replicate_chunk, METH_VARARGS|METH_KEYWORDS, py_net_replicate_chunk_doc},
536         {"finddc", (PyCFunction)py_net_finddc, METH_VARARGS, py_net_finddc_doc},
537         { NULL }
538 };
539
540 static void py_net_dealloc(py_net_Object *self)
541 {
542         talloc_free(self->mem_ctx);
543 }
544
545 static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
546 {
547         PyObject *py_creds, *py_lp = Py_None;
548         const char *kwnames[] = { "creds", "lp", "server", NULL };
549         py_net_Object *ret;
550         struct loadparm_context *lp;
551         const char *server_address = NULL;
552
553         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz",
554                                          discard_const_p(char *, kwnames), &py_creds, &py_lp,
555                                          &server_address))
556                 return NULL;
557
558         ret = PyObject_New(py_net_Object, type);
559         if (ret == NULL) {
560                 return NULL;
561         }
562
563         /* FIXME: we really need to get a context from the caller or we may end
564          * up with 2 event contexts */
565         ret->ev = s4_event_context_init(NULL);
566         ret->mem_ctx = talloc_new(ret->ev);
567
568         lp = lpcfg_from_py_object(ret->mem_ctx, py_lp);
569         if (lp == NULL) {
570                 Py_DECREF(ret);
571                 return NULL;
572         }
573
574         ret->libnet_ctx = libnet_context_init(ret->ev, lp);
575         if (ret->libnet_ctx == NULL) {
576                 PyErr_SetString(PyExc_RuntimeError, "Unable to initialize net");
577                 Py_DECREF(ret);
578                 return NULL;
579         }
580
581         ret->libnet_ctx->server_address = server_address;
582
583         ret->libnet_ctx->cred = cli_credentials_from_py_object(py_creds);
584         if (ret->libnet_ctx->cred == NULL) {
585                 PyErr_SetString(PyExc_TypeError, "Expected credentials object");
586                 Py_DECREF(ret);
587                 return NULL;
588         }
589
590         return (PyObject *)ret;
591 }
592
593
594 PyTypeObject py_net_Type = {
595         PyObject_HEAD_INIT(NULL) 0,
596         .tp_name = "net.Net",
597         .tp_basicsize = sizeof(py_net_Object),
598         .tp_dealloc = (destructor)py_net_dealloc,
599         .tp_methods = net_obj_methods,
600         .tp_new = net_obj_new,
601 };
602
603 void initnet(void)
604 {
605         PyObject *m;
606
607         if (PyType_Ready(&py_net_Type) < 0)
608                 return;
609
610         m = Py_InitModule3("net", NULL, NULL);
611         if (m == NULL)
612                 return;
613
614         Py_INCREF(&py_net_Type);
615         PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
616         PyModule_AddObject(m, "LIBNET_JOINDOMAIN_AUTOMATIC", PyInt_FromLong(LIBNET_JOINDOMAIN_AUTOMATIC));
617         PyModule_AddObject(m, "LIBNET_JOINDOMAIN_SPECIFIED", PyInt_FromLong(LIBNET_JOINDOMAIN_SPECIFIED));
618         PyModule_AddObject(m, "LIBNET_JOIN_AUTOMATIC", PyInt_FromLong(LIBNET_JOIN_AUTOMATIC));
619         PyModule_AddObject(m, "LIBNET_JOIN_SPECIFIED", PyInt_FromLong(LIBNET_JOIN_SPECIFIED));
620 }