Skip to content

Commit 4367e33

Browse files
committed
Add SSL changes from elixir-ecto#120
1 parent 05072d4 commit 4367e33

File tree

3 files changed

+93
-81
lines changed

3 files changed

+93
-81
lines changed

lib/tds/messages.ex

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ defmodule Tds.Messages do
1919
defrecord :msg_attn, []
2020

2121
# responses
22+
defrecord :msg_preloginack, [:response]
2223
defrecord :msg_loginack, [:redirect]
2324
defrecord :msg_prepared, [:params]
2425
defrecord :msg_sql_result, [:columns, :rows, :row_count]
@@ -60,6 +61,13 @@ defmodule Tds.Messages do
6061
# @tds_pack_prelogin 18
6162

6263
## Parsers
64+
def parse(:prelogin, packet_data, s) do
65+
response =
66+
packet_data
67+
|> Tds.Protocol.Prelogin.decode(s)
68+
69+
{msg_preloginack(response: response), s}
70+
end
6371

6472
def parse(:login, packet_data, s) do
6573
packet_data
@@ -235,16 +243,17 @@ defmodule Tds.Messages do
235243
encode(msg, env)
236244
end
237245

238-
defp encode(msg_prelogin(params: _params), _env) do
239-
version_data = <<11, 0, 12, 56, 0, 0>>
240-
version_length = byte_size(version_data)
241-
version_offset = 0x06
242-
version = <<0x00, version_offset::size(16), version_length::size(16)>>
243-
terminator = <<0xFF>>
244-
prelogin_data = version_data
245-
data = version <> terminator <> prelogin_data
246-
encode_packets(0x12, data)
246+
defp encode(msg_prelogin(params: opts), _env) do
247+
# version_data = <<11, 0, 12, 56, 0, 0>>
248+
# version_length = byte_size(version_data)
249+
# version_offset = 0x06
250+
# version = <<0x00, version_offset::size(16), version_length::size(16)>>
251+
# terminator = <<0xFF>>
252+
# prelogin_data = version_data
253+
# data = version <> terminator <> prelogin_data
254+
# encode_packets(0x12, data)
247255
# encode_header(0x12, data) <> data
256+
Tds.Protocol.Prelogin.encode(opts)
248257
end
249258

250259
defp encode(msg_login(params: params), _env) do

lib/tds/protocol.ex

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ defmodule Tds.Protocol do
133133
def checkout(%{sock: {mod, sock}} = s) do
134134
sock_mod = inspect(mod)
135135

136-
case :inet.setopts(sock, active: false) do
136+
case setopts(s.sock, active: false) do
137137
:ok ->
138138
{:ok, s}
139139

@@ -154,7 +154,7 @@ defmodule Tds.Protocol do
154154
def checkin(%{sock: {mod, sock}} = s) do
155155
sock_mod = inspect(mod)
156156

157-
case :inet.setopts(sock, active: :once) do
157+
case setopts(s.sock, active: :once) do
158158
:ok ->
159159
{:ok, s}
160160

@@ -382,13 +382,13 @@ defmodule Tds.Protocol do
382382

383383
:ok = :inet.setopts(sock, buffer: buffer)
384384

385-
case login(%{s | sock: {:gen_tcp, sock}}) do
385+
case prelogin(%{s | sock: {:gen_tcp, sock}}) do
386386
{:error, error, _state} ->
387387
:gen_tcp.close(sock)
388388
{:error, error}
389389

390-
r ->
391-
r
390+
other ->
391+
other
392392
end
393393

394394
{:error, error} ->
@@ -439,6 +439,26 @@ defmodule Tds.Protocol do
439439
end
440440
end
441441

442+
defp ssl_connect(%{sock: {:gen_tcp, sock}, opts: opts} = s) do
443+
{:ok, _} = Application.ensure_all_started(:ssl)
444+
:inet.setopts(sock, active: false)
445+
446+
case Tds.Tls.connect(sock, opts[:ssl_opts] || []) do
447+
{:ok, ssl_sock} ->
448+
state = %{s | sock: {:ssl, ssl_sock}}
449+
{:ok, state}
450+
451+
{:error, reason} ->
452+
error =
453+
Tds.Error.exception(
454+
"Unable to establish secure connection to server due #{inspect(reason)}"
455+
)
456+
457+
:gen_tcp.close(sock)
458+
{:error, error, s}
459+
end
460+
end
461+
442462
def handle_info({:udp_error, _, :econnreset}, s) do
443463
msg =
444464
"Tds encountered an error while connecting to the Sql Server " <>
@@ -451,10 +471,7 @@ defmodule Tds.Protocol do
451471
{:tcp, _, _data},
452472
%{sock: {mod, sock}, opts: opts, state: :prelogin} = s
453473
) do
454-
case mod do
455-
:gen_tcp -> :inet.setopts(sock, active: false)
456-
:ssl -> :ssl.setopts(sock, active: false)
457-
end
474+
setopts(s.sock, active: false)
458475

459476
login(%{s | opts: opts, sock: {mod, sock}})
460477
end
@@ -521,22 +538,16 @@ defmodule Tds.Protocol do
521538
def prelogin(%{opts: opts} = s) do
522539
msg = msg_prelogin(params: opts)
523540

524-
case msg_send(msg, s) do
525-
{:ok, s} ->
526-
{:noreply, %{s | state: :prelogin}}
527-
528-
{:error, reason, s} ->
529-
error(%Tds.Error{message: "tcp send: #{reason}"}, s)
530-
531-
any ->
532-
any
541+
case msg_send(msg, %{s | state: :prelogin}) do
542+
{:ok, s} -> login(s)
543+
any -> any
533544
end
534545
end
535546

536547
def login(%{opts: opts} = s) do
537548
msg = msg_login(params: opts)
538549

539-
case login_send(msg, s) do
550+
case login_send(msg, %{s | state: :login}) do
540551
{:ok, s} ->
541552
{:ok, %{s | state: :ready}}
542553

@@ -740,14 +751,19 @@ defmodule Tds.Protocol do
740751
end
741752
end
742753

754+
def message(:prelogin, msg_preloginack(response: response), _) do
755+
case response do
756+
{:login, s} -> {:ok, s}
757+
{:encrypt, s} -> ssl_connect(s)
758+
other -> other
759+
end
760+
end
761+
743762
def message(
744763
:login,
745764
msg_loginack(redirect: %{hostname: host, port: port}),
746-
%{opts: opts} = s
765+
%{opts: opts} = state
747766
) do
748-
# we got an ENVCHANGE:redirection token, we need to disconnect and start over with new server
749-
disconnect("redirected", s)
750-
751767
new_opts =
752768
opts
753769
|> Keyword.put(:hostname, host)
@@ -814,16 +830,17 @@ defmodule Tds.Protocol do
814830
end
815831

816832
# Send Command To Sql Server
817-
defp login_send(msg, %{sock: {mod, sock}, env: env} = s) do
833+
defp login_send(msg, %{sock: {mod, sock}, env: env, opts: opts} = s) do
818834
paks = encode_msg(msg, env)
835+
s = %{s | opts: clean_opts(opts)}
819836

820837
Enum.each(paks, fn pak ->
821838
mod.send(sock, pak)
822839
end)
823840

824841
case msg_recv(s) do
825842
{:disconnect, ex, s} ->
826-
{:error, ex, s}
843+
{:disconnect, ex, s}
827844

828845
buffer ->
829846
buffer
@@ -834,51 +851,33 @@ defmodule Tds.Protocol do
834851

835852
defp msg_send(
836853
msg,
837-
%{sock: {mod, sock}, env: env, state: state, opts: opts} = s
854+
%{sock: {mod, port}, env: env, state: state, opts: opts} = s
838855
) do
839-
:inet.setopts(sock, active: false)
856+
setopts(s.sock, active: false)
840857

841858
opts
842859
|> Keyword.get(:use_elixir_calendar_types, false)
843860
|> use_elixir_calendar_types()
844861

845-
{t_send, _} =
846-
:timer.tc(fn ->
847-
msg
848-
|> encode_msg(env)
849-
|> Enum.each(&mod.send(sock, &1))
850-
end)
851-
852-
{t_recv, {t_decode, result}} =
853-
:timer.tc(fn ->
854-
case msg_recv(s) do
855-
{:disconnect, _ex, _s} = res ->
856-
{0, res}
857-
858-
buffer ->
859-
:timer.tc(fn ->
860-
buffer
861-
|> IO.iodata_to_binary()
862-
|> decode(s)
863-
end)
862+
send_result =
863+
msg
864+
|> encode_msg(env)
865+
|> Enum.reduce_while(:ok, fn chunk, _ ->
866+
case mod.send(port, chunk) do
867+
{:error, reason} -> {:halt, {:error, reason}}
868+
:ok -> {:cont, :ok}
864869
end
865870
end)
866871

867-
stm = Map.get(s, :query)
868-
869-
if Keyword.get(s.opts, :trace, false) == true do
870-
Logger.debug(fn ->
871-
"[trace] [Tds.Protocod.msg_send/2] " <>
872-
"state=#{inspect(state)} " <>
873-
"send=#{Tds.Perf.to_string(t_send)} " <>
874-
"receive=#{Tds.Perf.to_string(t_recv - t_decode)} " <>
875-
"decode=#{Tds.Perf.to_string(t_decode)}" <>
876-
"\n" <>
877-
"#{inspect(stm)}"
878-
end)
872+
with :ok <- send_result,
873+
buffer when is_list(buffer) <- msg_recv(s) do
874+
buffer
875+
|> IO.iodata_to_binary()
876+
|> decode(s)
877+
else
878+
{:disconnect, _ex, _s} = res -> {0, res}
879+
other -> other
879880
end
880-
881-
result
882881
end
883882

884883
defp msg_recv(%{sock: {mod, pid}} = s) do
@@ -1152,4 +1151,11 @@ defmodule Tds.Protocol do
11521151
)
11531152
end
11541153
end
1154+
1155+
defp setopts({mod, sock}, options) do
1156+
case mod do
1157+
:gen_tcp -> :inet.setopts(sock, options)
1158+
:ssl -> :ssl.setopts(sock, options)
1159+
end
1160+
end
11551161
end

lib/tds/versions.ex

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
defmodule Tds.Version do
22
import Tds.Protocol.Grammar
33

4-
defstruct version: 0x74000004, str_version: "7.4"
4+
@default_version :v7_4
5+
@default_code 0x74000004
56

67
@versions [
7-
{0x71000001, "7.1"},
8-
{0x72090002, "7.2"},
9-
{0x730A0003, "7.3.A"},
10-
{0x730B0003, "7.3.B"},
11-
{0x74000004, "7.4"}
8+
{0x71000001, :v7_1},
9+
{0x72090002, :v7_2},
10+
{0x730A0003, :v7_3_a},
11+
{0x730B0003, :v7_3_b},
12+
{0x74000004, :v7_4}
1213
]
1314

1415
def decode(<<key::little-dword>>) do
15-
@versions
16-
|> List.keyfind(key, 0, "7.4")
16+
List.keyfind(@versions, key, 0, @default_version)
1717
end
1818

1919
def encode(ver) do
20-
val =
21-
@versions
22-
|> List.keyfind(ver, 1, 0x74000004)
23-
20+
val = List.keyfind(@versions, ver, 1, @default_code)
2421
<<val::little-dword>>
2522
end
2623
end

0 commit comments

Comments
 (0)