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