I am trying to send a string via http/post in Racket, this is what I tried so far after reading the Racket HTTP Client Documentation
#lang racket
(require net/http-client)
(define
myUrl "https://something.com")
(http-conn-send!
(http-conn-open
myUrl
#:ssl? #t)
#:version "1.1"
#:method "POST"
#:data "Hello")
But with this I receive the following error:
tcp-connect: connection failed
detail: host not found
address: https://www.w3.org/
port number: 443
step: 1
system error: nodename nor servname provided, or not known; errno=8
I tried it with several different adresses.
I am new to racket and programming in general and unable to figure out what I am missing.
In your example, the hostname is only the www.w3.org portion -- not including the scheme (http or https) nor any path. So for example this does work:
(http-conn-open "www.w3.com"
#:ssl? #t)
To make a post request, you could do this:
#lang racket
(require net/http-client)
(define-values (status headers in)
(http-sendrecv "www.w3.com"
"/"
#:ssl? #t
#:version "1.1"
#:method "POST"
#:data "Hello"))
(displayln status)
(displayln headers)
(displayln (port->string in))
(close-input-port in)
In Racket, a function can return multiple values. http-sendrecv returns three, and the define-values assigns each one to a variable.
net/http-client provides other functions to let you make a connection to a host, make multiple requests on that connection, then close the connection.
Related
I have 2 elixir applications.
In one of them I create a TCP server which listens on the port 8080 for packets. I know that it is defined correctly, because I connected to it with telnet and everything was working fine.
The problem appears when I try to connect to this TCP server from my other Elixir application.
That's how I connect to it
host = 'localhost'
{:ok, socket} = :gen_tcp.connect(host, 8080, [])
Not sure about the options that I should specify tho.
When trying to connect to it I get in the logs of the application with TCP server:
00:21:11.235 [error] Task #PID<0.226.0> started from MessageBroker.Controller terminating
** (MatchError) no match of right hand side value: {:error, :closed}
(message_broker 0.1.0) lib/message_broker/network/controller.ex:110: MessageBroker.Controller.read_line/1
(message_broker 0.1.0) lib/message_broker/network/controller.ex:101: MessageBroker.Controller.serve/1
(elixir 1.12.3) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib 3.12) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: #Function<0.126110026/0 in MessageBroker.Controller.loop_acceptor/1>
Args: []
At the line with
{:ok, data} = :gen_tcp.recv(socket, 0)
Any ideas, suggestions?
(MatchError) no match of right hand side value: {:error, :closed}
At the line with
{:ok, data} = :gen_tcp.recv(socket, 0)
The error message is saying that the function call :gen_tcp.recv(socket, 0) returned {:error, :closed} and that {:ok, data} does not match {:error, :closed}. If you call recv() on a socket that was closed by the other side, then recv() returns {:error, :closed}, and the other side will not be sending anymore data to that socket.
Not sure about the options that I should specify tho.
Yeah, those are pretty important. The basic tenet is that when you send data to a socket, you have no idea how many chunks the data will be split into. But, the client and server have to be able to know when to stop trying to read data because the end of the data has been reached. To solve the "indeterminate number of chunks problem", the client and server have to agree on some signal to mark the end of the data:
A newline?
The sender closes the socket?
Use an agreed upon number of bytes at the start of the data to specify the length of the data?
For #3, send() and recv() will automatically handle packaging and unpackaging the data for you if you simply tell gen_tcp how many bytes are necessary to specify the length of a message, for instance you can specify the option {:packet, 2}. If you do that, send() will automatically calculate the length of the data, then add 2 bytes to the front of the data containing the length of the data. Likewise, recv() will automatically read the first 2 bytes from the socket, then read the integer contained in those 2 bytes, say 64,999, then recv() will wait until it has read an additional 64,999 bytes from the socket, then it will return the whole 64,999 bytes of data.
How to send a message through TCP protocol to a TCP server using
gen_tcp in Elixir?
Here is an example of #1, where a newline is used to mark the end of the data:
TCP Server:
defmodule App1 do
def start_tcp_server do
port = 8080
{:ok, listen_socket} = :gen_tcp.listen(
port,
[
{:mode, :binary}, #Received data is delivered as a string (v. a list)
{:active, :false}, #Data sent to the socket will not be
#placed in the process mailbox, so you can't
#use a receive block to read it. Instead
#you must call recv() to read data directly
#from the socket.
{:packet, :line}, #recv() will read from the socket until
#a newline is encountered, then return.
#If a newline is not read from the socket,
#recv() will hang until it reads a newline
#from the socket.
{:reuseaddr, true} #Allows you to immediately restart the server
#with the same port, rather than waiting
#for the system to clean up and free up
#the port.
]
)
IO.puts "Listening on port #{port}...."
listen_loop(listen_socket)
end
defp listen_loop(listen_socket) do
{:ok, client_socket} = :gen_tcp.accept(listen_socket) #client_socket is created with the same options as listen_socket
handle_client(client_socket)
listen_loop(listen_socket)
end
defp handle_client(client_socket) do
case :gen_tcp.recv(client_socket, 0) do #Do not specify the number
#of bytes to read, instead write 0
#to indicate that the :packet option
#will take care of how many bytes to read.
{:ok, line} ->
#Echo back what was received:
IO.write("Server echoing back: #{line}")
:gen_tcp.send(client_socket, "Server received: #{line}") #line has a "\n" on the end
{:error, :closed} ->
{:ok, :client_disconnected}
end
end
end
TCP client:
defmodule App2 do
def send_data do
host = :localhost
port = 8080
{:ok, socket} = :gen_tcp.connect(host, port,
[
{:active, :false}, #Data sent to the socket will not be put in the process mailbox.
{:mode, :binary},
{:packet, :line} #Must be same as server.
]
)
:ok = :gen_tcp.send(socket, "Hi server!\n")
case :gen_tcp.recv(socket, 0) do
{:ok, line} ->
IO.puts ~s(Client got: "#{String.trim line}")
:ok = :gen_tcp.close(socket)
{:error, :closed} -> IO.puts("Server closed socket.")
end
end
end
Output in server window:
iex(1)> App1.start_tcp_server
Listening on port 8080....
Server echoing back: Hi server!
Output in client window:
ex(1)> App2.send_data
Client got: "Server received: Hi server!"
:ok
If you want to use {:error, :closed} to mark the end of the data, then you need to:
Specify the option {:packet, :raw} for both the client and the server socket.
After sending the data, close the socket by calling :gen_tcp.shutdown/2, which will ensure that all the data has been sent before closing the socket.
Loop over the recv(), saving each chunk returned by recv(), until the recv() returns {:error, :closed}, marking the end of the data, then your loop can return all the chunks it read. For instance:
defp get_data(socket, chunks) do
case :gen_tcp.recv(socket, 0) do #reads a chunk of indeterminate length from the socket
{:ok, chunk} ->
get_data(socket, [chunk | chunks])
{:error, :closed} ->
{:ok, Enum.reverse(chunks) }
end
end
You would call that function like this:
{:ok, data} = get_data(client_socket, [])
For more details on the gen_tcp options, see the following answer
Erlang client-server example using gen_tcp is not receiving anything
I've set up a hunchentoot server with ssl. I'd like regular http requests to be redirected to https.
It seems like some combination of hunchentoot:define-easy-handler and hunchentoot:redirect is the way to go, but I can't figure it out.
Here's what I have so far:
(defvar *https-handler*
(make-instance 'hunchentoot:easy-ssl-acceptor
:name 'ssl
:ssl-privatekey-file #P"/path/to/privkey.pem"
:ssl-certificate-file #P"/path/to/cert.pem"
:port 443))
(hunchentoot:start *https-handler*)
Yes, you can add simple http handler with redirect to ssl version:
(defvar *http-handler*
(make-instance 'hunchentoot:easy-acceptor
:name 'http
:port 80))
(hunchentoot:define-easy-handler (redir-to-ssl :uri (lambda (uri) t) :acceptor-names '(http)) ()
(hunchentoot:redirect "/" :protocol :https)) ; where magic happens
...and then start it too:
(hunchentoot:start *http-handler*)
This version redirects simply to index /.
Well, I am using the hunchentoot:*dispatch-table* directly. The way to redirect it independent of the path I discovered was to hunchentoot:redirect unless (hunchentoot:ssl-p) inside the handler. Most of my defuned handlers are wrapped inside a macro for authenthentication. So, I merely had to modify that macro, and then M-x slime-who-macroexpands -> C-c C-k.
(unless (hunchentoot:ssl-p)
(hunchentoot:redirect (hunchentoot:request-uri*)
:protocol :https))
If you need to indiscriminately redirect every HTTP request to HTTPS, using easy-acceptor is unnecessary. I'd suggest to define a specialized acceptor:
(defclass http-to-https-acceptor (hunchentoot:acceptor) ())
(defmethod hunchentoot:acceptor-dispatch-request ((acceptor http-to-https-acceptor) request)
(hunchentoot:redirect (hunchentoot:request-uri request)
:protocol :https))
Then at some point:
(hunchentoot:start (make-instance 'http-to-https-acceptor :port 80))
I want to hit an api (GET request) in clojure inside open shift pod with "no-proxy" but it gives me 404 (resource not found) although I passed correct header (for authorisation inside open shift pod). Same request giving 200 by curl command with noproxy in pod terminal. Please suggest.
Using Clojure API - clj-http.client
Below is Clojure code which is not working (giving 404):
(defn get-toggles []
(let [options {:headers {:x-jwt-payload "valid-value-passed"}}
response (client/get "correct-url" {:proxy-ignore-hosts "*" :proxy-port "*"} options)]))
Below is Curl hit inside pod terminal that is working fine (with noproxy only):curl -i --noproxy "*" "any-correct-url" -H "x-jwt-payload :valid-value"
Note: without "noproxy", we will get 401 thats why we are using it.
Looking at the source code, there seems no way to create a request that specifically uses no proxy at all.
I once ran into the same problem and ended up using Jsoup, a java library for making http requests and parsing HTML. I did not use the HTML parsing function, I sent requests to a Rest Api, but the Api of Jsoup is nice and well usable from Clojure. For example:
(ns something
(import
org.jsoup.Jsoup
java.net.Proxy
java.lang.Thread))
(defn api-call [url]
(->
(Jsoup/connect url)
(.proxy (Proxy/NO_PROXY))
(.ignoreContentType true)
(.get)
(.body)
(.text)))
I wrote the following OCaml code in order to make POST requests to an https server.
open Lwt
open Cohttp
open Cohttp_lwt_unix
try
let headers = Header.init ()
|> fun h -> Header.add h "content-type" "application/json" in
let body = Client.post
~headers
~body:(body_of_credentials nickname secret)
(uri_of_project project)
>>= fun (response, body) ->
let code = response |> Response.status |> Code.code_of_status in
Printf.printf "Response code: %d\n" code;
Printf.printf "Headers: %s\n" (response |> Response.headers |> Header.to_string);
body |> Cohttp_lwt.Body.to_string >|= fun body ->
Printf.printf "Body of length: %d\n" (String.length body);
body
in
let body = Lwt_main.run body in
print_endline ("Received body\n" ^ body)
with
| Tls_lwt.Tls_alert e ->
print_endline (Tls.Packet.alert_type_to_string e);
exit 1
But when executing it with CONDUIT_TLS=native CONDUIT_DEBUG=true COHTTP_DEBUG=true I get the following response :
Selected TLS library: Native
Resolver system: https://my_server/auth/tokens/ ((name https) (port 443) (tls true)) -> (TCP ((V4 xx.xxx.xx.xxx) 443))
HANDSHAKE_FAILURE
I've read all the google results (documentation, ocaml/cohttp/ocaml-tls lists and stack overflow questions) I could find about it, but nothing helps, so I would like to start from scratch here.
How can I get more details about this failure ?
In case it helps, I'm using the following opam configuration:
"lwt" {>= "4.1.0"}
"cohttp" {>= "1.1.1"}
"cohttp-lwt"
"cohttp-lwt-unix" {>= "1.1.1"}
"tls" {>= "0.9.2"}
EDIT:
As suggested by #ivg, I tried with CONDUIT_TLS=openssl but then I get the following error:
Selected TLS library: OpenSSL
Resolver system: https://my_server/auth/tokens/ ((name https) (port 443) (tls true)) -> (TCP ((V4 xx.xxx.xx.xxx) 443))
...: internal error, uncaught exception:
SSL: Method error
EDIT²:
As suggested in the following discussion: github.com/savonet/ocaml-ssl/issues/40 I added an opam pin to ssl-0.5.5: opam pin add ssl 0.5.5 in order to fix this error. Now I am able to post requests to my https server, but not with the pure ocaml implementation of tls.
You're getting this alert because the handshaking process (authentication) had failed. This alert means, that the peer is not authenticated (either the server or the client) and thus a secure connection could not be established.
To debug the issue, I would suggest first to ensure that everything works fine with conventional tools, e.g., openssl, wget, or curl.
If you're sure that your configuration is fine and this is a problem on the ocaml-tls part I would suggest to use the low-level interface of the ocaml-tls library. Apparently, conduit doesn't expose or use any of the tracing capabilities of ocaml-tls, so there is no other choice.
The alert type is projected from two more rich fatal and error types that bear much more information about the nature of the problem, cf. the following code that creates an alert and consider possible input values that lead to the HANDSHAKE_FAILURE alert.
To access the particular error or alert I would suggest you to use the Tracing capabilities of ocaml-tls. There are examples in the source repository that already enable tracing and should provide sufficient information. I hope those examples will fit into your use case.
i need to find the host name of a UNIX host whose IP is known with out login to that UNIX host
Use nslookup
nslookup 208.77.188.166
...
Non-authoritative answer:
166.188.77.208.in-addr.arpa name = www.example.com.
You can do a reverse DNS lookup with host, too. Just give it the IP address as an argument:
$ host 192.168.0.10
server10 has address 192.168.0.10
Another NS lookup utility that can be used for reversed lookup is dig with the -x option:
$ dig -x 72.51.34.34
; <<>> DiG 9.9.2-P1 <<>> -x 72.51.34.34
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12770
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1460
;; QUESTION SECTION:
;34.34.51.72.in-addr.arpa. IN PTR
;; ANSWER SECTION:
34.34.51.72.in-addr.arpa. 42652 IN PTR sb.lwn.net.
;; Query time: 4 msec
;; SERVER: 192.168.178.1#53(192.168.178.1)
;; WHEN: Fri Jan 25 21:23:40 2013
;; MSG SIZE rcvd: 77
or
$ dig -x 127.0.0.1
; <<>> DiG 9.9.2-P1 <<>> -x 127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11689
;; flags: qr aa ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;1.0.0.127.in-addr.arpa. IN PTR
;; ANSWER SECTION:
1.0.0.127.in-addr.arpa. 10 IN PTR localhost.
;; Query time: 2 msec
;; SERVER: 192.168.178.1#53(192.168.178.1)
;; WHEN: Fri Jan 25 21:23:49 2013
;; MSG SIZE rcvd: 63
Quoting from the dig manpage:
Reverse lookups -- mapping addresses to names -- are simplified by the
-x option. addr is an IPv4 address in dotted-decimal notation, or a colon-delimited IPv6 address. When this option is used, there is no
need to provide the name, class and type arguments. dig automatically
performs a lookup for a name like 11.12.13.10.in-addr.arpa and sets
the query type and class to PTR and IN respectively.
For Windows ping -a 10.10.10.10
For Windows, try:
NBTSTAT -A 10.100.3.104
or
ping -a 10.100.3.104
For Linux, try:
nmblookup -A 10.100.3.104
They are almost same.
It depends on the context. I think you're referring to the operating system's hostname (returned by hostname when you're logged in). This command is for internal names only, so to query for a machine's name requires different naming systems. There are multiple systems which use names to identify hosts including DNS, DHCP, LDAP (DN's), hostname, etc. and many systems use zeroconf to synchronize names between multiple naming systems. For this reason, results from hostname will sometimes match results from dig (see below) or other naming systems, but often times they will not match.
DNS is by far the most common and is used both on the internet (like google.com. A 216.58.218.142) and at home (mDNS/LLMNR), so here's how to perform a reverse DNS lookup: dig -x <address> (nslookup and host are simpler, provide less detail, and may even return different results; however, dig is not included in Windows).
Note that hostnames within a CDN will not resolve to the canonical domain name (e.g. "google.com"), but rather the hostname of the host IP you queried (e.g. "dfw25s08-in-f142.1e100.net"; interesting tidbit: 1e100 is 1 googol).
Also note that DNS hosts can have more than one name. This is common for hosts with more than one webserver (virtual hosting), although this is becoming less common thanks to the proliferation of virtualization technologies. These hosts have multiple PTR DNS records.
Finally, note that DNS host records can be overridden by the local machine via /etc/hosts. If you're not getting the hostname you expect, be sure you check this file.
DHCP hostnames are queried differently depending on which DHCP server software is used, because (as far as I know) the protocol does not define a method for querying; however, most servers provide some way of doing this (usually with a privileged account).
Note DHCP names are usually synchronized with DNS server(s), so it's common to see the same hostnames in a DHCP client least table and in the DNS server's A (or AAAA for IPv6) records. Again, this is usually done as part of zeroconf.
Also note that just because a DHCP lease exists for a client, doesn't mean it's still being used.
NetBIOS for TCP/IP (NBT) was used for decades to perform name resolution, but has since been replaced by LLMNR for name resolution (part of zeroconf on Windows). This legacy system can still be queried with the nbtstat (Windows) or nmblookup (Linux).
python -c "import socket;print(socket.gethostbyaddr('127.0.0.1'))"
if you just need the name, no additional info, add [0] at the end:
python -c "import socket;print(socket.gethostbyaddr('8.8.8.8'))[0]"
The other answers here are correct - use reverse DNS lookups.
If you want to do it via a scripting language (Python, Perl) you could use the gethostbyaddr API.
If you are specifically looking for a Windows machine, try below command:
nbtstat -a 10.228.42.57
You can use traceroute command as well.
http://linux.die.net/man/8/traceroute
just use the traceroute it will show you the routing path with host names (IPs resolved)
In most cases, traceroute command works fine. nslookup and host commands may fail.