-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconnect.c
More file actions
259 lines (224 loc) · 5.72 KB
/
Copy pathconnect.c
File metadata and controls
259 lines (224 loc) · 5.72 KB
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
/*-------------------------------------------------------------------------
* connect.c
* Connection oriented routines
*
* Copyright (c) 2026, Daniel Gustafsson <daniel@yesql.se>
*
*-------------------------------------------------------------------------
*/
#include <cc65.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ypsql.h"
#include "connect.h"
#include "pg_protocol.h"
#include "net.h"
static bool perform_auth(YPconn *conn, int auth_type);
static void state_machine(YPconn *conn);
void
connect_db(YPconn *conn)
{
bool ret;
Assert(conn->state == CONNECTION_NEEDED);
ret = net_connect(conn);
if (ret)
{
conn->state = CONNECTION_STARTED;
}
else
{
conn->state = CONNECTION_FAILED;
printf("* Connection to postgres failed\n");
quit(conn);
}
state_machine(conn);
}
static void
state_machine(YPconn *conn)
{
char *pkt;
uint16_t pkt_len;
char pkt_type;
bool expect = false;
keep_going:
net_poll(conn, expect);
switch(conn->state)
{
case CONNECTION_NEEDED:
connect_db(conn);
break;
case CONNECTION_STARTED:
/*
* We have a TCP connection to the server, and this is where the
* protocol allows for negotiating TLS or GSSAPI encryption, but
* since we don't support either we go straight to the startup
* message.
*/
pkt = build_startup_message(conn, &pkt_len);
if (!pkt)
goto error;
net_send(conn, pkt, pkt_len);
free(pkt);
pkt_len = 0;
conn->state = CONNECTION_AWAITING_RESPONSE;
expect = true;
goto keep_going;
case CONNECTION_AWAITING_RESPONSE:
{
int32_t msg_len;
int32_t auth_type;
/*
* Look at the first byte in the waiting message, or keep polling
* the network in case there is nothing to read. We just want to
* look at it so put it back on the buffer once read.
*/
net_read_byte(conn, &pkt_type);
/*
* The only valid messages from the server at this point are auth
* requests, error responses or protocol version negotiation. If
* any other message is passed, error out.
*/
if (pkt_type != MSG_AUTH_REQUEST &&
pkt_type != MSG_ERROR_RESPONSE &&
pkt_type != MSG_NEGOTIATE_PROTOCOL_VERSION)
{
printf("* ERROR: Expected auth request, got %c", pkt_type);
quit(conn);
}
/* TODO: message format/length validation */
net_read_int32(conn, &msg_len);
/*
* ErrorResponse Messages. The connection attempt has been rejected
* and the server will immediately close the connection. Display
* the message any useful diagnostocs and then error out as well.
*/
if (pkt_type == MSG_ERROR_RESPONSE)
{
read_ErrorResponse(conn);
/*
* If the server wants us to try another host then error out
* with an informative error message since we don't support
* that yet.
*/
if (strcmp(conn->sql_state, ERRCODE_CANNOT_CONNECT_NOW) == 0)
{
printf("* ERROR: ERRCODE_CANNOT_CONNECT_NOW\n");
quit(conn);
}
}
/* Not supported as of yet */
else if (pkt_type == MSG_NEGOTIATE_PROTOCOL_VERSION)
{
printf("* ERROR: MSG_NEGOTIATE_PROTOCOL_VERSION\n");
quit(conn);
}
/* At this point we know it is an authentication request */
Assert(pkt_type == MSG_AUTH_REQUEST);
net_read_int32(conn, &auth_type);
if (!perform_auth(conn, auth_type))
{
printf("* ERROR: perform_auth OOM\n");
quit(conn);
}
expect = true;
goto keep_going;
}
/*
* If we received an auth_request_ok message then we are done with
* setting up the authentiction. Next up is reading the connection
* setup from the server.
*/
case AUTHENTICATION_COMPLETE:
expect = false;
net_read_byte(conn, &pkt_type);
/*
* I have yet to figure out why the code need to sleep before
* reading the next message, but when not doing it the message
* length calculation overflow and things behave badly. Some
* timing related bug is lurking in these backwaters.
*/
if (pkt_type == MSG_BACKEND_KEY_DATA)
{
printf(".");
sleep(1);
read_BackendKeyData(conn);
goto keep_going;
}
if (pkt_type == MSG_PARAMETER_STATUS)
{
printf(".");
sleep(1);
read_ParameterStatus(conn);
goto keep_going;
}
if (pkt_type == MSG_ERROR_RESPONSE)
{
printf(".");
read_ErrorResponse(conn);
goto keep_going;
}
if (pkt_type == MSG_NOTICE_RESPONSE)
{
printf(".");
read_NoticeResponse(conn);
goto keep_going;
}
if (pkt_type == MSG_READY_FOR_QUERY)
{
printf(".");
read_ReadyForQuery(conn);
conn->state = CONNECTION_COMPLETE;
printf("\n* Connected to PostgreSQL\n");
return;
}
printf("* Unknown pkt_type %i\n", pkt_type);
goto keep_going;
default:
/* TODO: Errorhandling */
break;
}
error:
/* TODO: Errorhandling */
return;
}
static bool
perform_auth(YPconn *conn, int auth_type)
{
char *packet;
uint16_t packet_len;
switch (auth_type)
{
case MSG_AUTH_REQUEST_OK:
printf("* Authentication complete\n* Reading connection setup ");
conn->state = AUTHENTICATION_COMPLETE;
return true;
case MSG_AUTH_REQUEST_MD5:
printf("* MD5 auth\n");
quit(conn);
case MSG_AUTH_REQUEST_PASSWORD:
if (build_PasswordMessage(conn, &packet, &packet_len) == YP_OK)
{
net_send(conn, packet, packet_len);
free(packet);
return true;
}
break;
case MSG_AUTH_REQUEST_KRB4:
case MSG_AUTH_REQUEST_KRB5:
case MSG_AUTH_REQUEST_CRYPT:
case MSG_AUTH_REQUEST_GSS:
case MSG_AUTH_REQUEST_GSS_CONT:
case MSG_AUTH_REQUEST_SSPI:
case MSG_AUTH_REQUEST_SASL:
case MSG_AUTH_REQUEST_SASL_CONT:
case MSG_AUTH_REQUEST_SASL_FINT:
printf("* ERROR: unsupported auth: %i\n", auth_type);
break;
default:
printf("* ERROR: unknown auth: %i\n", auth_type);
break;
}
return false;
}