Вчерновую накидал cross-process singleton на named pipes, плиз потыкайте носом
Ясен пень, что до юзабельного состояния пилить и пилить: вынести в отдельный класс, добавить разные имена для разных сессий, не забыть про обработку ошибок, запуск в фоновом потоке, прерывание работы и т.д. и т.п.
Меня сейчас интересуют в первую очередь упущенные нюансы/неверно указанные параметры/безопасность. Например, я почти уверен, что в коде дырка: не блокируются удалённые соединения. В понедельник буду на работе, проверю.
код
using System;
using System.IO;
using System.IO.Pipes;
using System.Security.AccessControl;
using System.Security.Principal;
namespace ProcessSingleInstance
{
class Program
{
static void Main(string[] args)
{
string[] data = new[] { "a", "aaaaa", "qweqeqweqeqweqwert" };
NamedPipeServerStream server = null;
try
{
server = CreateServerStream();
}
catch (UnauthorizedAccessException)
{
}
if (server != null)
{
RunServer(server);
}
else
{
RunClient(data);
}
}
// критиковать здесь.
// Сервер должен _только_ читать данные;
// не должен позволять соединения вне текущей сессии, в т.ч. удалённыеprivate static NamedPipeServerStream CreateServerStream()
{
PipeSecurity security = new PipeSecurity();
security.AddAccessRule(
new PipeAccessRule(
WindowsIdentity.GetCurrent().User,
PipeAccessRights.Synchronize
| PipeAccessRights.ReadAttributes // DONTTOUCH: Required. I don't know why.
| PipeAccessRights.Write,
AccessControlType.Allow));
NamedPipeServerStream server = new NamedPipeServerStream(
"server",
PipeDirection.In, 1,
PipeTransmissionMode.Message, PipeOptions.WriteThrough, // Не уверен. Может, всё-таки использовать буфер?
4096, 4096,
security, HandleInheritability.None);
return server;
}
// и здесьprivate static NamedPipeClientStream CreateClientStream()
{
NamedPipeClientStream client = new NamedPipeClientStream(
".", "server",
PipeDirection.Out, PipeOptions.WriteThrough, TokenImpersonationLevel.None, HandleInheritability.None);
return client;
}
private static void RunServer(NamedPipeServerStream server)
{
while (true)
{
Console.WriteLine("Wait...");
BinaryReader r = new BinaryReader(server);
server.WaitForConnection();
try
{
int count = r.ReadInt32();
for (int i = 0; i < count; i++)
{
Console.WriteLine(r.ReadString());
}
}
catch (IOException)
{ }
server.Disconnect();
}
}
private static void RunClient(string[] data)
{
using (NamedPipeClientStream client = CreateClientStream())
{
client.Connect();
using (BinaryWriter w = new BinaryWriter(client))
{
try
{
w.Write(data.Length);
foreach (var item in data)
{
w.Write(item);
}
client.WaitForPipeDrain();
}
catch (IOException)
{ }
}
}
}
}
}
Re: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>// не должен позволять соединения вне текущей сессии, в т.ч. удалённые
В другой сессии может быть запущен процесс от имени юзера текущей сессии. И даже на другой машине, если это доменная учётка.
S>PipeTransmissionMode.Message, PipeOptions.WriteThrough, // Не уверен. Может, всё-таки использовать буфер?
Ага Through применим только для byte-mode, и только если клиент и сервер находятся на разных машинах. А в Вашем случае этот флаг будет просто проигнорирован как не имеющий смысла.
"Нормальные герои всегда идут в обход!"
Re[2]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, hardcase, Вы писали:
H>А это на COM+ нельзя разве решить?
Оверкилл. Возни с ним много (регистрация та же) а преимуществ 0. Да ещё использоваться будет в софте с "установкой копированием".
Re[2]: [.Net 4, cross-process singleton] Покритикуйте код
JR>В другой сессии может быть запущен процесс от имени юзера текущей сессии. И даже на другой машине, если это доменная учётка.
Спс. Я быстренько покопался во внутренностях: кажется, что фильтрацию по IP/данным к NamedPipeServerStream клиента не прикрутить. Либо переходить на WCF (а там это возможно будет?) либо попробовать что-то ещё (что? в голову ничего разумного не приходит), либо забить и вернуться к реализации на глюкодырявом ремотинге
JR>Ага Through применим только для byte-mode, и только если клиент и сервер находятся на разных машинах. А в Вашем случае этот флаг будет просто проигнорирован как не имеющий смысла.
От оно чё...
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, hardcase, Вы писали:
H>>А это на COM+ нельзя разве решить? S>Оверкилл. Возни с ним много (регистрация та же) а преимуществ 0. Да ещё использоваться будет в софте с "установкой копированием".
Вообще-то в случае COM без регистрации вполне можно обойтись, вот здесь
я описывал, как. На шарпе, думаю, это тоже реализуемо. Для захвата приоритета можно использовать стандартную схему с мьютексом, а интерфейс сервера раздавать через Running Object Table (ROT). На пайпах требование по поводу чужих сессий реализовать, по-моему, намного сложнее.
"Нормальные герои всегда идут в обход!"
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S> Либо переходить на WCF (а там это возможно будет?)
На WCF пайпы как раз работают только локально (если только в 4.0 не поменяли), но вот фильтровать по сессиям вряд-ли удастся просто, если вообще.
S> вернуться к реализации на глюкодырявом ремотинге
Но-но Когда этот "ремонтник" появился, создатели его позиционировали как супер-пупер киллера всех остальных технологий удалённого взаимодействия. Ну прям как WCF сейчас
"Нормальные герои всегда идут в обход!"
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Jolly Roger, Вы писали:
JR>На WCF пайпы как раз работают только локально (если только в 4.0 не поменяли), но вот фильтровать по сессиям вряд-ли удастся просто, если вообще.
[нецензурно-унылое выражение разочарования в несовершенстве мира].
Здравствуйте, Sinix, Вы писали:
S>Что ж делать-то?
Ну дык каждый ищет там, где ему светлее Я-бы мьютекс и COM использовал — для работы без регистрации код один раз пишется, а ROT позволяет исключить подключения из чужих сессий, а удалённым он вообще недоступен.
"Нормальные герои всегда идут в обход!"
Re[6]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Jolly Roger, Вы писали:
JR>Ну дык каждый ищет там, где ему светлее
C COMом знаком только заочно, связываться сейчас ради решения "один раз сделал — всю жизнь мучаешься" не рискну. Ну, и смысл откатываться с устаревшего, но работающего remoting'а на ещё более легаси, но, возможно, работающий ещё лучше COM+ —
Пойду копаться в the new shining code, шоб его...
Спасибо!
Re[5]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>Меня сейчас интересуют в первую очередь упущенные нюансы/неверно указанные параметры/безопасность. Например, я почти уверен, что в коде дырка: не блокируются удалённые соединения. В понедельник буду на работе, проверю.
Проверил. Дырка. Гугль молчит
Re[2]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>>Например, я почти уверен, что в коде дырка: не блокируются удалённые соединения. В понедельник буду на работе, проверю.
S>Проверил. Дырка. Гугль молчит
Попробуйте в PipeSecurity добавить явный запрет для Network users.
S>с WCF граблей не меньше. Но удалённые соединения допускает.
Пайпы-то работают. Я имел в виду, что это искусственно зарезано в NamedPipeBinding
"Нормальные герои всегда идут в обход!"
Re[2]: [.Net 4, cross-process singleton] Покритикуйте код
По поводу локальных чужих сессий — в принципе, можно попробовать имперсонировать клиента, вытащить из маркера его SessionId (GetTokenInformation + TokenSessionId) и сравнить с текущей сессией.
"Нормальные герои всегда идут в обход!"
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Jolly Roger, Вы писали:
JR>По поводу локальных чужих сессий — в принципе, можно попробовать имперсонировать клиента, вытащить из маркера его SessionId (GetTokenInformation + TokenSessionId) и сравнить с текущей сессией.
1) Да не, это уже оверкилл будет. И так хватит.
2) По наводке: http://msdn.microsoft.com/en-us/library/aa379626.aspx
TokenSessionId
...
In a non-Terminal Services environment, the session identifier is zero.
Огромное спасибо!
Re[3]: [.Net 4, cross-process singleton] Покритикуйте код
Здравствуйте, Sinix, Вы писали:
S>UPD. Вроде бы, нужно использовать AuthenticationId из TOKEN_STATISTICS.
Да нет, вероятно. AuthenticationId — это идентификатор Logon Session, это не то, его, к примеру, можно задать, используя для логона LsaLogonUser.
S>In a non-Terminal Services environment, the session identifier is zero
Ну да, если у Вас нет Terminal Services environment, то и пользовательская сессия одна-единственная => вопрос о сессиях снимается автоматически.
S>Аж стыдно, что не додумался до такого простого решения
Да ладно, мне оно тоже не сразу в голову пришло Мало иметь информацию в мозгах, нужно ещё чтобы она в нужный момент всплыла на поверхность из всего прочего хлама
"Нормальные герои всегда идут в обход!"
Re[5]: [.Net 4, cross-process singleton] Покритикуйте код
JR>Да нет, вероятно. AuthenticationId — это идентификатор Logon Session, это не то, его, к примеру, можно задать, используя для логона LsaLogonUser.
Увы да, нужен именно logon session sid. Если его добыть — даём права только сессии — вуаля Сейчас осталась единственная дырка: локально заходим одновременно под двумя пользователями, под первым запускаем сервер, под вторым (от имени первого) — клиента.
Попробую вытащить ч/з WMI и забью. Тем более что, как я понимаю, в WCF будет абсолютно та же уязвимость.