I'm implementing proxy support for an osx app.
I've created a custom NSURLProtocol and it is working perfectly for a proxy without authentication. Tried with both CCProxy and FreeProxy on a Windows computer in the local network.
However when proxy authentication is on, any request in the first few seconds works perfectly then the connection goes from ESTABLISHED to CLOSE_WAIT in 5 seconds. The proxy shows 0 connections again and, after that in the app any HTTP request will get a 407, even though the proxy-auth header is pre-set.
My code looks like this :
// set the auth fields
CFStringRef usernameRef = (__bridge CFStringRef)appProxyPrefs.proxyUserName;
CFStringRef passwordRef = (__bridge CFStringRef)appProxyPrefs.proxyPassword;
CFHTTPMessageAddAuthentication(copyOfOriginal, nil, usernameRef, passwordRef, kCFHTTPAuthenticationSchemeBasic, YES);
...
// useless
CFHTTPMessageSetHeaderFieldValue(copyOfOriginal, (__bridge CFStringRef)#"Connection", (CFStringRef)(#"Keep-Alive"));
...
// create stream, callback, schedule
// apply proxy settings to the stream
if (isNoProxyOverride)
CFReadStreamSetProperty(myReadStream, kCFStreamPropertyHTTPProxy, (__bridge CFTypeRef)(noProxyDict));
else
CFReadStreamSetProperty(myReadStream, kCFStreamPropertyHTTPProxy, (__bridge CFTypeRef)(manualProxyDict));
...
CFReadStreamSetProperty(myReadStream, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
if (!CFReadStreamOpen(myReadStream)) { // error }
else
{
// check if there is a prev stream
if (currentStream != nil)
{
[currentStream close];
currentStream = nil;
}
currentStream = (__bridge NSInputStream *)(myReadStream);
}
As you see I tried to store the previous conenction in a static inputstream and releasing it only after I open a new one, but seems useless.
Also tried setting the underlying socket to keep-alive in kCFStreamEventOpenCompleted as suggested in NSStream TCP Keep-alive iOS , still without success.
Why does the connection close ? How could I debug it or make it work ?
Is the connection's fault the proxy goes craxy ?
Thanks.
Edit 1:
Edit 2: It seems it has to do with HTTPS... If I change the the server to be plain http instead of https it will work perfectly.
Related
I am very new to programming with ESP8266 and can't get this to work.
I am successfully connected to the internet.
With the <ESP8266HTTPClient.h> I am writing the following code:
void loop() {
if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status
HTTPClient http; //Declare object of class HTTPClient
http.begin("https://api.entur.io/journey-planner/v2/graphql"); //Specify request destination
http.addHeader("Content-Type", "application/json"); //Specify content-type header
http.addHeader("ET-Client-Name", "student-bussAPI"); //Identification requested by the API
int httpCode = http.POST("{\"query\":\"{authorities{id}}\"}"); //Send the request
String payload = http.getString(); //Get the response payload
Serial.println(httpCode); //Print HTTP return code
Serial.println(payload); //Print request response payload
http.end(); //Close connection
} else {
Serial.println("Error in WiFi connection");
}
delay(30000); //Send a request every 30 seconds
}
the code runs with serial output -1
I have tested the query request on postman, and it worked fine there.
The "-1" you are getting is caused by the attempt to establish a connection to a strictly HTTPS service via HTTP.
As the code examples in the ESP8266HttpClient library explain, "httpCode will be negative on error" - what you are seeing is the printing of a http status code of -1.
The easiest way to achieve what you are trying to do here (Query a HTTPS API via the ESP8266httpClient library) would be to connect to it using the current SHA-1 fingerprint of the certificate used by the service as a second parameter of your call to "http.begin".
Example to answer your specific question:
HTTPClient http; //Declare object of class HTTPClient
const char* host = "https://api.entur.io/journey-planner/v2/graphql";
const char* fingerprint ="Current sha-1 fingerprint goes here";
http.begin(host, fingerprint); //Specify request destination and fingerprint for cert to make it do HTTPS
http.addHeader("Content-Type", "application/graphql"); //Content type here is important; Its not JSON
http.addHeader("ET-Client-Name", "Esp8266-BitBrb");
I should also mention that the specific API you are using here has a strict check on content type, so you should change your header to match the data type the API accepts, "application/graphql", or else you'll have trouble :)
Incidentally, i have the full code from a project i did last year with the same service you are using (Hi, fellow Norwegian and EnTur user) available here: https://www.bitbrb.com/electronics/graphql-querying-a-bus
I see they got a new certificate earlier this year, other than that you should be able to cut and paste.
good luck :)
I've heard about Google's plan of modernizing OAuth interactions described here: https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html
Then I was looking at the sample desktop application for Windows found here: https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp.
It's pretty simple and it was working, but once I started Visual Studio without elevated privileges (as a non-admin), I experienced that the HttpListener was not able to start because of the following error: "Access Denied".
It turned out that starting an HttpListener at the loopback address (127.0.0.1) is not possible without admin rights. However trying localhost instead of 127.0.0.1 lead to success.
I found that there is a specific command that allows HttpListener to start at the given address (and port):
netsh http add urlacl url=http://+:80/MyUri user=DOMAIN\user
But it also can be only executed with admin rights, so it's not an option.
Still localhost seems to be the best shot but OAuth 2.0 for Mobile & Desktop Apps states the following regarding this section:
See the redirect_uri parameter definition for more information about the loopback IP address. It is also possible to use localhost in place of the loopback IP, but this may cause issues with client firewalls. Most, but not all, firewalls allow loopback communication.
This is why I'm a bit suspicious to use localhost. So I'm wondering what is the recommended way of Google in this case, as I'm not intending to run our application as administrator just for this reason.
Any ideas?
You can use TcpListener for instance instead of HttpListener. It does not need elevation to listen.
The following is a modified excerpt of this sample:
https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp
// Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";
// Creates a redirect URI using an available port on the loopback address.
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
output("redirect URI: " + redirectURI);
// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
authorizationEndpoint,
System.Uri.EscapeDataString(redirectURI),
clientID,
state,
code_challenge,
code_challenge_method);
// Opens request in the browser.
System.Diagnostics.Process.Start(authorizationRequest);
// Waits for the OAuth authorization response.
var client = await listener.AcceptTcpClientAsync();
// Read response.
var response = ReadString(client);
// Brings this app back to the foreground.
this.Activate();
// Sends an HTTP response to the browser.
WriteStringAsync(client, "<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please close this window and return to the app.</body></html>").ContinueWith(t =>
{
client.Dispose();
listener.Stop();
Console.WriteLine("HTTP server stopped.");
});
// TODO: Check the response here to get the authorization code and verify the code challenge
The read and write methods being:
private string ReadString(TcpClient client)
{
var readBuffer = new byte[client.ReceiveBufferSize];
string fullServerReply = null;
using (var inStream = new MemoryStream())
{
var stream = client.GetStream();
while (stream.DataAvailable)
{
var numberOfBytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
if (numberOfBytesRead <= 0)
break;
inStream.Write(readBuffer, 0, numberOfBytesRead);
}
fullServerReply = Encoding.UTF8.GetString(inStream.ToArray());
}
return fullServerReply;
}
private Task WriteStringAsync(TcpClient client, string str)
{
return Task.Run(() =>
{
using (var writer = new StreamWriter(client.GetStream(), Encoding.UTF8))
{
writer.Write("HTTP/1.0 200 OK");
writer.Write(Environment.NewLine);
writer.Write("Content-Type: text/html; charset=UTF-8");
writer.Write(Environment.NewLine);
writer.Write("Content-Length: " + str.Length);
writer.Write(Environment.NewLine);
writer.Write(Environment.NewLine);
writer.Write(str);
}
});
}
By default there is a URL pattern http://+:80/Temporary_Listen_Addresses/ which is allowed for all users (\Everyone)
You can use this as a prefix for your listener. More generally (to avoid collisions with other listeners) you should generate a URL under Temporary_Listen_Addresses (e.g. using a GUID) and use that as your listener prefix.
Unfortunately, a sysadmin can use netsh http to delete this entry or to restrict its usage to only certain users. Also, this does not appear to support listening for an HTTPS request as there is no corresponding ACL entry for port 443.
An admin can list all these permitted URL patterns using netsh http show urlacl as a command.
I'm using rabbitmq/rabbitmq-objc-client. Is there a way how to check if connection was opened successfully? I see there is RMQConnectionDelegate but there are only methods that are called when an error appears. I'm looking for something like
RMQConnection *conn = [[RMQConnection alloc] /*...*/];
if ([conn isOpen]) {
/* ... */
}
You can do this:
[conn start:^{ // code to execute when connection established }];
But note that the client is async and all channel operations are placed onto a queue for execution after the connection is established.
I've added a note to the issue Michael opened: https://github.com/rabbitmq/rabbitmq-objc-client/issues/101
I have a locally hosted WCF service and a silverlight 5 app that communicates with it. By default silverlight tries to obtain the cross domain policy file over HTTP when making calls to the WCF service. I need to change this so that the policy file is served over net.tcp port 943 instead.
I have setup a local tcp listener that serves up the policy file over port 943 and i have followed this technique whereby i make a dummy socket connection in order to obtain the policy file over tcp as it is only retrieved once per application lifetime. The tcp server is being hit as expected and i am getting SocketError property value as Success (though i must note, the first time i hit the tcp server after starting the listener, the result is always access denied).
From what i can tell, the policy file is either invalid as the silverlight application as still unable to connect or the above mentioned technique does not work with silverlight 5.
What i would like to know is if what i am doing is possible & im doing it correctly, otherwise if there is an alternative means to have the policy file successfully downloaded over tcp and removing the need for retrieving it over HTTP.
Thanks
I wrote a long post about hosting silverlight in WPF - and using WCF with a http listener here:
How can I host a Silverlight 4 application in a WPF 4 application?
Now while not directly answering your question, it does show how to create a http version of the policy file.
I have also written something that serves up a policy listener over port 943, but I can't find where I posted the source - so I'll keep digging. As far as I remember though, silverlight does a cascade find of the policy file, if it doesn't get a connection on port 80, it'll then look on port 943.
I hope this is of some help somewhere.
Ok, here is the policy listener I had for net.TCP transport i.e. not HTTP based. I presume you have sorted this by now, sorry for the delay. It may well be of use to someone else now.
I was looking for the MS thing that said they cascade from HTTP to TCP, however, I can't, and therefore have to assume it was bunk and then changed.
Either way, if you call using a net.TCP service, and want a listener for it, this code should help:
#region "Policy Listener"
// This is a simple policy listener
// that provides the cross domain policy file for silverlight applications
// this provides them with a network access policy
public class SocketPolicyListener
{
private TcpListener listener = null;
private TcpClient Client = null;
byte[] Data;
private NetworkStream netStream = null;
private string listenaddress = "";
// This could be read from a file on the disk, but for now, this gives the silverlight application
// the ability to access any domain, and all the silverlight ports 4502-4534
string policyfile = "<?xml version='1.0' encoding='utf-8'?><access-policy><cross-domain-access><policy><allow-from><domain uri='*' /></allow-from><grant-to><socket-resource port='4502-4534' protocol='tcp' /></grant-to></policy></cross-domain-access></access-policy>";
// the request that we're expecting from the client
private string _policyRequestString = "<policy-file-request/>";
// Listen for our clients to connect
public void Listen(string ListenIPAddress)
{
listenaddress = ListenIPAddress;
if (listener == null)
{
listener = new TcpListener(IPAddress.Parse(ListenIPAddress), 943);
// Try and stop our clients from lingering, keeping the socket open:
LingerOption lo = new LingerOption(true, 1);
listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger,lo);
}
listener.Start();
WaitForClientConnect();
}
private void WaitForClientConnect()
{
listener.BeginAcceptTcpClient(new AsyncCallback(OnClientConnected), listener);
}
public void StopPolicyListener()
{
if (Client.Connected)
{
// Should never reach this point, as clients
// are closed if they request the policy
// only clients that open the connection and
// do not submit a policy request will remain unclosed
Client.Close();
}
listener.Stop();
}
public void RestartPolicyListener()
{
listener.Start();
}
// When a client connects:
private void OnClientConnected(IAsyncResult ar)
{
if (ar.IsCompleted)
{
// Get the listener that handles the client request.
TcpListener listener = (TcpListener)ar.AsyncState;
// End the operation and display the received data on
// the console.
Client = listener.EndAcceptTcpClient(ar);
// Try and stop our clients from lingering, keeping the socket open:
LingerOption lo = new LingerOption(true, 1);
Client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lo);
// Set our receive callback
Data = new byte[1024];
netStream = Client.GetStream();
netStream.BeginRead(Data, 0, 1024, ReceiveMessage, null);
}
WaitForClientConnect();
}
// Read from clients.
public void ReceiveMessage(IAsyncResult ar)
{
int bufferLength;
try
{
bufferLength = Client.GetStream().EndRead(ar);
// Receive the message from client side.
string messageReceived = Encoding.ASCII.GetString(Data, 0, bufferLength);
if (messageReceived == _policyRequestString)
{
// Send our policy file, as it's been requested
SendMessage(policyfile);
// Have to close the connection or the
// silverlight client will wait around.
Client.Close();
}
else
{
// Continue reading from client.
Client.GetStream().BeginRead(Data, 0, Data.Length, ReceiveMessage, null);
}
}
catch (Exception ex)
{
throw new Exception(Client.Client.RemoteEndPoint.ToString() + " is disconnected.");
}
}
// Send the message.
public void SendMessage(string message)
{
try
{
byte[] bytesToSend = System.Text.Encoding.ASCII.GetBytes(message);
//Client.Client.Send(bytesToSend,SocketFlags.None);
Client.GetStream().Write(bytesToSend,0, bytesToSend.Length);
Client.GetStream().Flush();
}
catch (Exception ex)
{
throw ex;
}
}
}
#endregion
I have a launch daemon which I want to ask for status information from a user app. I implemented a client-server model (with the daemon as server) using unix sockets as described here: OS X - Communication between launch daemon and launch agent
In fact it works well, when I run the daemon as a user process (for debugging), but it will fail when it is actually launched as root.
I have read the TN on Daemons and Agents and the Daemon & Services Programming Guide. However, I could not find decent information how the socket must be used in a launch daemon.
I am confused by several things:
Must I specify the socket in the launch daemon plist file? And how?
If the socket is specified in the plist, does that change the way I need to create the socket in code?
What path would be good for the unix socket? The Technical Note recommends /var/run but I guess a user process may not write there, or can it?
Is there maybe a easier way to do IPC between daemon and client?
What is the best way to log the daemon output. I tried NSLog but it seems not work...
I am also unsure if my socket code is correct. Maybe someone more experienced can tell me if I'm on the right track here. I have the following code in the daemon to initialize the unix socket:
#define SOCKETNAME "/var/run/com.company.myApp.socket"
- (void) startServer {
//remove any prev socket
unlink(SOCKETNAME);
CFSocketContext CTX = { 0, (__bridge void *)(self), NULL, NULL, NULL };
CFSocketRef unixSocket = CFSocketCreate(NULL, PF_UNIX, SOCK_STREAM, 0,
kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &CTX);
if (unixSocket == NULL) {/*log and return*/}
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCKETNAME);
addr.sun_len = strlen(addr.sun_path) + sizeof (addr.sun_family);
NSData *address = [ NSData dataWithBytes: &addr length: sizeof(addr) ];
if (CFSocketSetAddress(unixSocket, (__bridge CFDataRef) address) != kCFSocketSuccess) {
NSLog(#"CFSocketSetAddress() failed\n");
CFRelease(unixSocket);
}
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, unixSocket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
CFRelease(sourceRef);
CFRunLoopRun();
}
void AcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
CTServerController* selfServerController = (__bridge CTServerController*) info;
//NSLog(#"acceptCallBack");
//...
}
you should not specify the socket in the plist, you know the concrete place, the server knows, the client knows, that should be enought
the socket specified in the plist is for launching the deamon/agent ondemand, you do not need that in a basic case
you can use any path, if your daemon starts first (usually the case) it has all the privilege to set the correct rights on the socket to let anybody (or a given user, group) read, read and write or any given rights you wish, i'm sure you just forgot to let the client write/read the unix socket
i think on OSX the unix socket IPC is a perfect, easy solution (you have many other choices too of course, xpc, mach messages, etc.)
you can define where to go the stdout and stderr of a daemon in it's plist (StandardOutPath, StandardErrorPath keys)