1 | #!/usr/bin/env python
|
---|
2 | # -*- coding: utf-8 -*-
|
---|
3 |
|
---|
4 | # simple illustration client/server pair; client program sends a string
|
---|
5 | # to server, which echoes it back to the client (in multiple copies),
|
---|
6 | # and the latter prints to the screen
|
---|
7 | # this is the client
|
---|
8 | import socket
|
---|
9 | import sys
|
---|
10 | import struct
|
---|
11 | import re
|
---|
12 | import time
|
---|
13 | import md5
|
---|
14 |
|
---|
15 | PORT = 9101
|
---|
16 | BUFSIZE = 1048576
|
---|
17 |
|
---|
18 | # BNET signals
|
---|
19 | BNET_SIGNALS = {
|
---|
20 | 'BNET_EOD' : -1, # End of data stream, new data may follow
|
---|
21 | 'BNET_EOD_POLL' : -2, # End of data and poll all in one
|
---|
22 | 'BNET_STATUS' : -3, # Send full status
|
---|
23 | 'BNET_TERMINATE' : -4, # Conversation terminated, doing close()
|
---|
24 | 'BNET_POLL' : -5, # Poll request, I'm hanging on a read
|
---|
25 | 'BNET_HEARTBEAT' : -6, # Heartbeat Response requested
|
---|
26 | 'BNET_HB_RESPONSE' : -7, # Only response permited to HB
|
---|
27 | 'BNET_PROMPT' : -8, # Prompt for subcommand
|
---|
28 | 'BNET_BTIME' : -9, # Send UTC btime
|
---|
29 | 'BNET_BREAK' : -10, # Stop current command -- ctl-c
|
---|
30 | 'BNET_START_SELECT' : -11, # Start of a selection list
|
---|
31 | 'BNET_END_SELECT' : -12, # End of a select list
|
---|
32 | 'BNET_INVALID_CMD' : -13, # Invalid command sent
|
---|
33 | 'BNET_CMD_FAILED' : -14, # Command failed
|
---|
34 | 'BNET_CMD_OK' : -15, # Command succeeded
|
---|
35 | 'BNET_CMD_BEGIN' : -16, # Start command execution
|
---|
36 | 'BNET_MSGS_PENDING' : -17, # Messages pending
|
---|
37 | 'BNET_MAIN_PROMPT' : -18, # Server ready and waiting
|
---|
38 | 'BNET_SELECT_INPUT' : -19, # Return selection input
|
---|
39 | 'BNET_WARNING_MSG' : -20, # Warning message
|
---|
40 | 'BNET_ERROR_MSG' : -21, # Error message -- command failed
|
---|
41 | 'BNET_INFO_MSG' : -22, # Info message -- status line
|
---|
42 | 'BNET_RUN_CMD' : -23, # Run command follows
|
---|
43 | 'BNET_YESNO' : -24, # Request yes no response
|
---|
44 | 'BNET_START_RTREE' : -25, # Start restore tree mode
|
---|
45 | 'BNET_END_RTREE' : -26, # End restore tree mode
|
---|
46 | }
|
---|
47 |
|
---|
48 | import clibbac
|
---|
49 |
|
---|
50 | class ConsoleError(IOError):
|
---|
51 | pass
|
---|
52 |
|
---|
53 | class Console:
|
---|
54 | """Class to connect with the bacula director as a console client
|
---|
55 | """
|
---|
56 |
|
---|
57 | HELLO = r"Hello %s calling"
|
---|
58 | HELLO_OK = "1000 OK"
|
---|
59 | RXP_RESP = re.compile(
|
---|
60 | """^auth\scram-md5(?P<compatible>c?)\s
|
---|
61 | (?P<challenge>\S+)\s
|
---|
62 | ssl=(?P<tls>\d)""",
|
---|
63 | re.VERBOSE)
|
---|
64 |
|
---|
65 | def __init__(self, host="bacula", port=PORT):
|
---|
66 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
---|
67 | self.socket.connect((host, port))
|
---|
68 | self.last_state = 0
|
---|
69 |
|
---|
70 | def __del__(self):
|
---|
71 | """Destructor closes the network socket"""
|
---|
72 | self.socket.close()
|
---|
73 |
|
---|
74 | def _send(self, data):
|
---|
75 | """Prepend size of data before the data string
|
---|
76 | and send it trough the socket"""
|
---|
77 | size = struct.pack("!i", len(data))
|
---|
78 | self.socket.send(size + data)
|
---|
79 |
|
---|
80 | def _recv(self, bufsize=BUFSIZE):
|
---|
81 | """Recieve data from socket and strip the prepended
|
---|
82 | data size"""
|
---|
83 | r = self.socket.recv(4)
|
---|
84 | size = struct.unpack("!i", r)[0]
|
---|
85 | if size < 0:
|
---|
86 | self.last_state = size
|
---|
87 | r = ""
|
---|
88 | elif size + 4 > BUFSIZE:
|
---|
89 | raise ConsoleError("Buffer to small: %i" %size)
|
---|
90 | else:
|
---|
91 | r = self.socket.recv(size)
|
---|
92 | return r
|
---|
93 |
|
---|
94 | def _recv_all(self):
|
---|
95 | """Gets all lines of a request"""
|
---|
96 | r = ""
|
---|
97 | s = self._recv()
|
---|
98 | while s:
|
---|
99 | r += s
|
---|
100 | s = self._recv()
|
---|
101 | return r
|
---|
102 |
|
---|
103 | def login(self, username, password):
|
---|
104 | pw = md5.md5(password).hexdigest()
|
---|
105 |
|
---|
106 | self._send(self.HELLO % username + "\n")
|
---|
107 | response = self._recv()
|
---|
108 |
|
---|
109 | # Challenge from director
|
---|
110 | m = self.RXP_RESP.search(response)
|
---|
111 | challenge = clibbac.hmac_md5(m.group("challenge"), pw)
|
---|
112 | self._send(clibbac.bin_to_base64(challenge[:16], False))
|
---|
113 | resp = self._recv()
|
---|
114 | if not resp.startswith(self.HELLO_OK):
|
---|
115 | raise ConsoleError("Challenge: %s" % resp)
|
---|
116 |
|
---|
117 | # now from client site
|
---|
118 | now = time.time()
|
---|
119 | respond = clibbac.bin_to_base64(
|
---|
120 | clibbac.hmac_md5("<%10.10f@bconsole>" % now, pw)[:16],
|
---|
121 | False)
|
---|
122 | self._send("auth cram-md5 <%10.10f@bconsole> ssl=0\n" % now)
|
---|
123 | resp = self._recv().rstrip("\x000")
|
---|
124 | if resp != respond:
|
---|
125 | print len(resp)
|
---|
126 | print len(respond)
|
---|
127 | raise ConsoleError("Respond: %s expected: %s" % (resp, respond))
|
---|
128 | self._send("1000 OK auth\n")
|
---|
129 |
|
---|
130 | return self._recv()
|
---|
131 |
|
---|
132 | def version(self):
|
---|
133 | """Returns the bacula director version string"""
|
---|
134 | self._send("version")
|
---|
135 | return self._recv_all().split("\n")[0]
|
---|
136 |
|
---|
137 | def dotstatus(self):
|
---|
138 | self._send(".status dir current")
|
---|
139 | return self._recv_all()
|
---|
140 |
|
---|
141 | if __name__ == "__main__":
|
---|
142 | try:
|
---|
143 | host = sys.argv[1]
|
---|
144 | port = int(sys.argv[2])
|
---|
145 | except:
|
---|
146 | host = "bacula-devel"
|
---|
147 | port = 9101
|
---|
148 |
|
---|
149 | print sys.path
|
---|
150 |
|
---|
151 | console = Console(host, port)
|
---|
152 | console.login("python", "dass-it")
|
---|
153 |
|
---|
154 | print console.version()
|
---|
155 | print console.dotstatus()
|
---|
156 |
|
---|
157 |
|
---|
158 | #....Hello python calling
|
---|
159 | #...7auth cram-md5 <1938954300.1244114680@bacula-dir> ssl=0
|
---|
160 | #....XRx1dCJU36/UJD/l7TBZZC....
|
---|
161 | #1000 OK auth
|
---|
162 | #...4auth cram-md5 <238682207.1244114679@bconsole> ssl=0
|
---|
163 | #....klIi6CczoW89Bl/wCA04wA....
|
---|
164 | #1000 OK auth
|
---|
165 | #...31000 OK: bacula-dir Version: 3.0.1 (30 April 2009)
|
---|